On a longer term(post 1.0) Nice might get a const keyword. Anyone who's is interested in writing a proposal should look at this proposal for java: http://david.tribble.com/text/javaconst.html
It's a very good starting point in my opinion.
An extension to Java exists that add constness: http://pag.lcs.mit.edu/constjava/
-- ArjanB
How would this be different from let?
-- IsaacGouy - 17 Sep 2003
When using let or final in java you can't change the reference. And const is like in c++ that the object that is referenced may not be changed.
-- ArjanB
I never much liked "const" as it exists in C++. The problem is that an object might appear to be mutable or immuable depending on where you are in the code. So a "const" object might change due to action somewhere else in the program.
Much better to have true immutable objects. Perhaps mutable and immutable versions of the same type, similar to !T and ?T.
Final instance variables in Java provide finer control since you can make an object partially immutable.
-- BrianSlesinsky - 21 Nov 2003
How and where to announce new releases?
The release itself is done on sourceforge. It includes release notes and the changelog (copy-pasted from the changelog in CVS). An item automatically appears under http://sourceforge.net/new/, but I don't think this will reach many people, given the number of releases every day. It is an option to write a news item on the Nice project: a few are selected by people and put on the main website. There is also the Java foundry, where news items can be submitted.
Nice has also a project page on freshmeat and in the GNU directory.
Lambda the Ultimate is a good place to announce Nice (major releases only), or to discuss a new feature. Is there a LtU regular who would like to be in charge of this?
Newsgroups: anybody using these? (comp.lang.???).
comp.lang.java.advocacy -- IsaacGouy - 27 Aug 2003
Other places?
-- DanielBonniot - 30 May 2003
This can be used, after possible editing to adapt it to the audience, to give a short presentation of Nice:
Nice is a new object-oriented programming language based on Java. It incorporates features from functional programming, and puts into practice state-of-the-art results from academic research. Among the advanced features: parametric types, anonymous functions, multi-methods, tuples, optional parameters to methods, design by contract, detection of many errors during compilation (in particular, concerning casts and null references). This results in more expressivity, modularity, and type safety.
Added various missing Java constructs: super, synchronized statements, Java 1.4 style assertions, and Java 1.5-compatible generic classes. Also implemented Design By Contract features: pre- and post-conditions for methods. Method dispatch can be done on boolean and integer values.
I was just wondering, when Nice interacts heavily with Java, the resultant code contains
too many curly braces which are not nice at all, as exceptions must be handled. Most of
the time, however, we know specifically what the exception ought to be. Why not have a perl
like syntax for handing those?
BufferedReader? br = new BufferedReader?(new FileReader?(file)) || return false;
-- ImamTashdidUlAlam - 21 Oct 2004
Tests are performed continuously to check that the compiler is working properly.
The results are published on the website.
The tests involve bootstraping the compiler (compiling the compiler with itself), running the testsuite, and compiling some other programs written in Nice. Here is the documentation about WritingAutomatedTests.
The tests are run on different machines, and different configurations (for instance, different JDK versions). The process of adding a new TestMachine is documented (it would be nice to have one machine using a case-insensitive file system, as found in Windows and Mac OS X).
-- DanielBonniot - 22 Jun 2003
class A{}
class B extends A{}
interface I<A T>{}
class X<T | A <: T> implements I<T>{}
void foo(I<B>);
foo(@X){}
Type error in method body "foo":
mlsub.typing.TypingEx: Not satisfiable [NO MESSAGE]
Sometime if you forget a ";" or a ")" or something else then you get a very long parse exception messages. It's not helpfull to get 20 lines of possible alternatives of wich half are multi-token alternatives.
No good example at the moment.
-- ArjanB
Here's a particularly awful example of this problem:
This takes long enough to report a message that you're likely to think the compiler's gone into a loop, especially if it's in a real source file not just this tiny example. Finally, it produces more than 700 lines of parse alternatives, most of which look something like these:
I just tested the compiler on the real-world code that inspired this example to see how long it would actually take to report the error - I gave up after 20 minutes. :-)
-- BrynKeller - 05 May 2003
I just fixed the problem of these extreme long error messages. The parser has some difficulties with generating error messages in case of a lookahead.
But I haven't a good idea yet how to improve the normal parse exception messages.
-- ArjanB
In general, it seems like desugared code should never be shown in error messages. Only show code fragments when necessary, and only show code that actually appears in the source files.
Code:
class A { ?Integer n; }
void main(String [] args) {
let a2 = new A(n: new Integer(2));
Integer n2 = a2.n != null ? a2.n : new Integer(1);
}
Old message:
The value if(`!=`(a2.n(), null))
a2.n()
else
new Integer(1)
cannot be assigned to n2 because it might be null.
New message:
The value a2.n != null ? a2.n : new Integer(1)
cannot be assigned to n2 because it might be null.
or
Attempt to assign a possibly-null value to n2
This last form could be chosen as soon as the value assigned is "big".
We can keep track of recent improvements. This way, there can be a debate about the change, if somebody doesn't like it, or thinks it could be further improved.
Each entry should follow this format: code, old message, new message. To add an entry, you can copy paste the template at the bottom, and fill it in.
The value null cannot be assigned to l because it might be null.
To allow l to contain the null value, it should be declared as:
?java.util.List<java.lang.String> l
class A {
String B;
String C = "test";
?String D;
}
void main(String[] args) {new A();}
Old message:
Class test.A has the following fields:
java.lang.String B, java.lang.String C = "Test", ?java.lang.String D
Please provide values for the fields, at least for those with no default value.
The syntax is:
new test.A(field1: value1, ..., fieldN: valueN)
New message:
Fields of class test.A require initial values.
Use the following syntax:
new test.A(B: value, D: value)
Class test.Foo has the following fields:
java.lang.String B
java.lang.String C = "test"
?java.lang.String D
class A {
String B;
String C = "test";
?String D;
}
void main(String[] args) {new A(B: "foo");}
Old message:
Class test.A has the following fields:
java.lang.String B, java.lang.String C = "Test", ?java.lang.String D
Please provide values for the fields, at least for those with no default value.
The syntax is:
new test.A(field1: value1, ..., fieldN: valueN)
New message:
Fields of class test.A require initial values.
These fields are missing:
?java.lang.String D
Maybe a year from now, one of the most obvious special things about Nice will turn up in releases of Java and C# ( More on Generics in the CLR ).
Reading this bold statement from the Brew project "We are currently developing Brew as a successor language to Java" made me wonder "What's the vision for Nice?".
In the same way that people seem to understand that Jython is good for glueing a program together out of Java components, what would it be that they should understand Nice is good for?
Is Nice capable of stepping up with a solution to this: The Impedance Imperative Tuples + Objects + Infosets = Too Much Stuff!
Whatever the Vision thang turns out to be, my guess is that it will give a purpose that makes decisions on language features more straightforward.
-- IsaacGouy - 18 Sep 2003
The way I see it now, Nice is a generalist programming language. So it would be more a "successor language to Java" than a niche, specialized language. There are many improvements in Nice besides genericity. I think they mostly fit under the slogan "safety, modularity, expressivity". Is this satisfactory? Is there a clearer or more striking way to put it?
-- DanielBonniot - 30 Sep 2003
"safety, modularity, expressivity"
OK can that slogan be used to drive implementation decisions?
Let's take Safety:
- instead of nicec having a -strict option, shouldn't it be strict by default? The -strict option affects only the calling of java methods and it's not default now because of the burden to first time users. If most of the java api is given the correct nice types then -strict will probably the default.
- is there a way to 'fix' Java's silent integer overflow / modular arithmetic? Very difficult to do without virtual machine support
- one or two folk were interested in Option Types until they saw cast(null), to them it seemed like an escape hatch that would inevitably be misused. In a few cases cast(null) is needed. We will try to reduce them. And maybe should cast(null) not be mentioned in the manual
Modularity:
- I wish an abstract interface could be used to type fields (just a simple-minded programmer) todo
Expressivity:
- type inference for polymorphic method vars (maybe one day) todo
- labelled tuples? Will they be immutable? What can you do with them that can't be done with classes or normal tuples?
- literal XML? Can you elaborate?
OK maybe I just want to try Erik Meijer's research language ;-)
But I know why - database + program + web.
"safety, modularity, expressivity" as a slogan it's a bit too abstract, these are all good things but rather difficult to judge without doing a deal of programming. "What problem does this solve" is much easier for simple-minded programmers to understand ;-)
-- IsaacGouy? - 03 Oct 2003
Comments added in italic. -- ArjanB - 06 Oct 2003
Just found out that C# has a compiler option and keyword support for checked arithmetic -- IsaacGouy - 06 Oct 2003
- literal XML? Can you elaborate?
The motivation is laid out here:
http://www.research.microsoft.com/~emeijer/Papers/XML2003/xml2003.html
-- IsaacGouy - 16 Oct 2003
I think from a big-picture standpoint, Nice is looking to be a better (easier and more reliable) way of generating .jar files than Java. But it's more of an incremental improvement than a paradigm shift.
Good IDE support (including refactoring) will be critical to get any traction at all. Back when I used Emacs, I tried out new languages all the time and didn't really give anything up by doing so. But now that I use IntelliJ?, switching to a better language doesn't look like a win if it also means using a more primitive IDE. And I expect the percentage of vi and emacs programmers will decline.
So, maybe the way to go is to design the language while working on the Eclipse plugin at the same time. Not that it should be Eclipse-specific, but language features should be designed taking the programmer's total user interface in mind.
-- BrianSlesinsky - 30 Dec 2003
Although developing an IDE/plugin at the same time could be ideal, it's not practical at the moment. A good plugin would take even more work than the compiler itself so with only 2 working on the compiler regularly it's not an option.
The difficult choice between the IDEs and the limited experience of us with advanced IDEs are ohter reasons not to develop it at the same time.
How do you think that the language could be influced by a plugin?
Nice makes much more programming styles possible than Java so that we don't know anything yet about good refactorings and pratical idioms. I think we need to know more about the usage of Nice before a plugin can be useful.
-- ArjanB - 31 Dec 2003
It's true that there are still several important core language features to work on, so I cannot focus on IDEs yet. On the other hand, I agree that good IDE support will be crucial to make the language accepted and easier to develop with. We already have a core EclipsePlugin, and it would be great if one or several people interested stepped in to add more advanced features like refactoring. I'm very willing to help by providing the appropriate API in the compiler, so that the work can be focused on providing the features to the IDE user. Concerning the choice of an IDE, I think Eclipse is a good one, given it's already important user base and it's open source status. Of course it's quite fine if somebody wants to work on integration with another IDE too.
-- DanielBonniot - 26 Jan 2004
Not much activity here. But I thought I should refresh this discussion:
Big Picture: I've looked at the "languages for the JVM" and "languages for the CLR" materials on the web. It's clear that there are two trends that Nice is a response to.
Trend 1: Java/C# isn't enough. It must be complemented with something easier to use, etc. Lots of languages are responses to this, but usually these are oriented toward "scripting", that is, it is assumed that if you need to write a real or fast program you'll flop back into Java or C#. Groovy is a good example of this. Jython similarly. There are a pile of these. Often they are aimed at convenience also. Typically they don't much care about type systems.
Trend 2: Java/C# are too hard to program in. We can improve on them by creating a more modern language with type inference, etc. These are fewer in number. The languages of note here are Scala, Boo, and Nice. These languages use type inference. THey go after the expressive-power problems of Java/C#. E.g., multiple return values, type-system restrictions, run-time null-pointer exceptions, etc. There are no compromises in performance in this area. THe programs written need to be just as fast as the corresponding program written in Java/C#.
The community of people with appreciation for Trend 2 is quite a bit smaller than trend 1. The two are blurred terribly because once you add type inference you can end up with programs that are just as clean and dense as in dynamically typed languages (I've verified this with translation of Python code to Nice. If you cuddle your "else", e.g., write lines like "} else {" to keep the braces from taking up whole lines, then you get the same number of lines in Nice as you do in Python. ) So long as we don't get diluted into a "scripting" solution this blurring is good. In fact I think a read-eval-print-loop interactive environment would be a great addition to Nice, allowing it to basically displace Python for my uses.
So the big-picture goal for Nice should be: 1) fix java expressive power issues. Easier to program in. Good for "real" programs, not just scripting. 2) eliminate the need for the dual-language solutions like Java + Jython for real programs and scripting. In short: fix java, displace python.
Other languages we should learn from: Scala and Boo are the two in the same category. I'm not sure what's to learn there. Of the scripting-side languages Groovy shares enough syntax that perhaps some of its features can be copied. It appears to have a community and some traction.
-- MikeBeckerle - 22 Dec 2004
It would be good to allow a different syntax for method application in
some circumstances. Specifically, it would be nice if:
foo
{
baz();
}
could expand into
foo(()=>
{
baz();
});
Basically, if a function call is followed immediately by a block, then the
block is converted to a void->A function, where A is the return type of
the function, and treated as the last argument to the function call.
This part is easy to implement. But what if the block isn't the last argument or there are multiple blocks?
This would allow us to add features like C#'s 'using' keyword, without having
to modify the syntax every time we want a new one:
Do you have other examples than 'using'?
<A> A using(Disposable resource, void->A func)
{
try
{
return func();
}
finally
{
resource.dispose();
}
}
let stream = new FileInputStream("foo");
let value = using(stream) { char c = stream.read(); }
Of course, we might also want something other than a void->A function,
perhaps we'd like to have a version of 'using' that doesn't require to
first bind the used object to a name. We could solve this by instead
expanding the block function into a function which takes exactly the
same arguments, with the same names, that the named function takes:
//Assume an abstract interface for disposables here, makes the
//typing nicer.
<Disposable A, B> B using(A resource, A->B func)
{
try
{
return func(resource: resource);
}
finally
{
resource.dispose();
}
}
I think it's more consistent to allow argument of function types to be named.
<Disposable A, B> B using(A resource, (resource A)->B func)
{
try
{
return func(resource);
}
finally
{
resource.dispose();
}
}
so now we can do
using(new FileInputStream("foo"))
{
//use the file input stream with the name 'resource':
char c = resource.read();
}
which the compiler will translate to:
using(new FileInputStream("foo"), (FileInputStream resouce) =>
{
//use the file input stream with the name 'resource':
char c = resource.read();
});
-- BrynKeller - 31 Oct 2003
The problem with this is that you can't choose the name of the variable so you end up with an general name.And it would get problematic when you nest 'using statements'. I'd rather write something like the following although I have no idea how that would look like on the implementation side of the function.
using(myFile = new FileInputStream("foo"))
{
//do something with it
}
added some comments in italic -- ArjanB - 08 Nov 2003
I don't know why anyone hasn't recognized it yet, but you are getting closer and closer to the syntax of Ruby. I like the stronly typed-ness of Nice, but Ruby has tons of cool syntax sugar that I would also like to see in Nice. Blocks (or Closures) are only one of them. Many classes in Ruby have been designed to make heavy use of blocks passed to methods. I don't like some of the Ruby syntax, such as the yield call. But still, it would be good for the Nice compiler developers to take a close look at Ruby, and to adopt what they like. It's interesting that Nice has already adopted quite a bit of what Ruby has. They are both interesting languages. I can see, though, that Nice can more easily handle enterprise level projects than Ruby. Here're some links to learn about Ruby: http://www.ruby-lang.org/en/ http://www.sofer.com/ruby/ http://www.rubycentral.com/
-- TroyHeninger - 12 Nov 2003
Seems more obvious to look at groovy.
Maybe Nice has more in common with FlowJavaJiazzi and MultiJava than with scripting languages.
-- IsaacGouy - 31 Dec 2003
Maybe I should look at C# using before commenting but...
How am I supposed to figure out that this
loop(5) { doSomeThing(); }
is shorthand for this
loop(5, () => { doSomeThing(); } );
That doesn't look like the same kind of thing; this does
loop(5, { doSomeThing(); } );
Smalltalk avoids the ugly syntax because blocks (Smalltalk anonymous functions) are objects with defined methods, so we can write something equivalent to { doSomeThing(); }.loop(5) and still have consistent language.
loop(5) { doSomeThing(); } doesn't seem to fit with the existing forms in the language - it doesn't look like function application; it looks like a keyword but it isn't a keyword.
-- IsaacGouy - 08 Jan 2004
I don't think you should look at it as a shorthand for something else. It's more like a user defined statement.
BTW we are considering making () => doSomeThing(); } equivalent to { doSomeThing(); )
-- ArjanB - 08 Jan 2004
Does user defined statement solve some problem? (Whatever happened to my spirit of adventure!)
Here are some funky apply-to-all expressionsstatic void Main() { qs = Queens.queens(7); qs.{ Queens.printboard(it); }; Console.ReadLine(); }
-- IsaacGouy - 09 Jan 2004
The implicit 'it' variable has problems when you try to nest apply-to-all.
apply-to-all looks superfluous for Nice because of the alternatives.
-- ArjanB - 09 Jan 2004
Fine with me - I find the qs.{ Queens.printboard(it); }; too cryptic.
-- IsaacGouy - 14 Jan 2004
Notice in the syntax that is currently implemented you have this scoping problem:
let f = new FileOutputStream("foo");
using(f)
{ write(f, getBytes("Hello World!"));
}
write(f, getBytes("Hey, Please don't let me write to a closed stream"));
The scope of myFile should be inside the using block, not outside the block, so that the second call to write above should fail. This is the same reasoning that allows:
for (int i = 0; i < x; ++i) { }
as a shortcut for:
int i;
for (i = 0; i < x; ++i) { }
For scoping, the example with let above is not particular to the way BlockCallSyntax is implemented, it's the normal rule. We are working on generalizing the for (int i = ...) syntax to arbitrary expressions. This will make the language more coherent, and in particular allow to use it with calls like using, which will allow you to have the scoping that you rightfully want. -- DanielBonniot - 01 Feb 2004
What is the advantage of using(f) { ... } over using(f, { ... });? I agree it is prettier but it looks like a language construct like synchronized instead of a library function. The second form seems more intuitive and is extensible to multiple block parameters:
<T, U, V | U <: T, V <: T>
T if2(boolean b, void -> U thenExpr, void -> V elseExpr)
= b ? thenExpr() : elseExpr();
if2(x < y, { thenDoSomething(); }, { elseDoSomething(); });
BTW, if2 is how the "built in" conditional statements are done in Cecil Language.
-- BrianSmith - 01 Feb 2004
The good thing about BlockCallSyntax is that, although its use will at first seem look like language constructs, it is actually a user-definable form. So after you learn that fact, you see the language as more uniform and coherent. And it will be interesting to see what uses users find for it. For instance, Bryn used it quite cleverly I think in his article about his logging library.
It's true that it does not apply to multiple block parameters, but that could be handled later by extending the syntax. -- DanielBonniot - 01 Feb 2004
Maybe cleaner concurrency (Scala)
spawn {
while(p<n) { p=p+1; buffer put(p); }
}
-- IsaacGouy - 01 Feb 2004
By amusing coincidence, the same code would look like this with the concurrency library I'm working on for Nice:
async {
while(p<n) { p=p+1; buffer.put(p); }
}
-- BrynKeller - 02 Feb 2004
Now my expectation is that I can generally apply this more uniform and coherent language like this ;-)
let a = { return 5 + 2; };
println ( { return 5 + 2; } );
BTW we are considering making () => doSomeThing(); } equivalent to { doSomeThing(); )
-- IsaacGouy - 23 Feb 2004
Yes, you can do that even now. IMO, this is a problem with the type of println. It should not accept functional values. I just sent a message to the nice-info mailing list about this.
Currently, you can write:
let a = () => "hello";
// these print "#<procedure gnu.expr.ModuleMethod>"
println(a);
println( () => "hello" );
// these both print "hello"
println(a());
println( (() => "hello")() );
With the second kind of block call syntax, you could write:
let a = { "hello" };
// these print "#<procedure gnu.expr.ModuleMethod>"
println(a);
println( { "hello" } );
// these both print "hello"
println(a());
println( { "hello" }() );
My proposal is that the type system should not allow the calls that print "#<procedure gnu.expr.ModuleMethod."
-- BrianSmith - 24 Feb 2004
I disagree. I see no reason we shouldn't be able to print functional values, even though the message is not (currently?) very informative. What is it about printing methods that bothers you?
-- BrynKeller - 24 Feb 2004
The resulting nice.jar is in share/java. You can install it system-wide using
make install
(Make sure you have a reasonably up-to-date version of groff - 1.18.1 or later should be OK at least.)
(If something still goes wrong, `make fixpoint' may help to recompile some stuff, or `make world', which also runs some tests.)
-- TWikiGuest - 06 Apr 2005
Does "Modification of the type system for subclasses with a different number of type parameters than the parent." mean that I could do:
class B extends A<Foo> {}
rather than the current approximation:
class B {
A<Foo> b;
equals(that@B) = b.equals(that);
// other wrappers for pass through functions to make B act like an A<Foo>
}
Yet B exists primarily so that functions can require a B rather than allow in any kind of A<Foo>
-- RohanHart - 08 Aug 2003
Yes,
class B extends A<Foo>
would be possible. In such cases the compiler only have to remember that class B has a hidden type parameter Foo.
For complex cases and subclasses with more type parameters as the superclass, i don't know if it will be possible maybe can Daniel tell more about that.
-- ArjanB
The current behaviour of numeric type is to silently overflow. In some cases this is what is needed, and in some cases you don't really care, because it is guaranteed no overflow will occur. However, there are also situations in which you would like a runtime exception to be thrown when overflows/underflows occur (hello, Isaac!).
The JVM does not directly support this arithmetic mode. However, at least for non-long types, it is possible to perform operations on a larger JVM type than the declared type, and then check for overflows. This has of course a small performance hit, but much less than using objects (BigInteger? for instance). So it might be possible to add new types that have overflow detection semantic and good performance. I'll focus on int, but this should be doable for shorter types too. What cases are useful in practice?
Since we want to retain the current semantics for int, we should add a new type, with the meaning "a 32 bit signed integer not modulo 2**32" (that is, which throws an exception when an overflow occurs). I'm not sure what the best name for it would be. integer is a possibility, but I was also considering it for infinite length integers (that is an alias for BigDecimal?). Other ideas: checkedInt (not pretty), checked-int (better, but a new convention for type names), cint (cryptic). Let's use cint in this document, it's short :-) how about checked as a modifier, as in "checked int"?
Regarding typing, int should be a subtype of cint, because you gain "safety" by considering an int as a cint. On the other hand, going from cint to int would later allow overflows to occur, so it should not be automatic. I would also think that cint should be a subtype of long, does that make sense?
Regarding implementation, one possibility is to represent cint as long in the JVM, and to check the result of each operation. Another possibility is to represent cint values as int, but operations on cint values would be performed on long, and the result checked before conversion back to int. The former might be more efficient (less conversions), while the later is more economous in space, which is especially interesting if you have an array of cint. Alternatively, is it possible to detect all/most overflows by working on int only (for instance, for addition, if both numbers are positive, there is overflow if the number is negative)?
-- DanielBonniot - 21 Dec 2003
Overflow detection is possible with int for addition and substraction by sign check. For multiplication, i am not quite shure if even long would be enough. -- TWIkiGuest?
The multiplication of two 32 bit numbers fits on 64 bits without overflow (because log(a*b) = log(a)+log(b)). So it seems we could use int to represent cint, and use temporarily a long for multiplication.
Could somebody actually write the operations as Nice methods (using int as the type)? So a int checkedAddition(int,int) method, ... -- DanielBonniot
I don't think that overflow checking should a part of primitive types because it's not clear when the check should happen(when the result is assigned to a cint or one or both of the operands are cint).
Whether to do checking is a property of operators so checked methods or code blocks make more sense.
I have doubts about the usefulness of this proposal and this is VM issue and not a language thing.
-- ArjanB - 22 Dec 2003
One way to look at it is that there are three kinds of integer types: modulo integers (higher bits are discarded), limited length (checked) integers and inifite length integers (BigInteger?). Mathematically, the first one makes sense (computing a hash value for instance), and of course the last one too. Checked integers would be useful when modulo is clearly incorrect, but you expect that 32 (or 64) bits will be enough. Using BigInteger? would incur a significant performance penalty, but modulo integers would hide errors if they occur.
So from a language point of view, all three types make sense. Therefore it is not a bug that the JVM does modulo arithmetic, it's useful. If we can use it to provide checked arithmetics too, then all the better.
Rgarding when checks happen, it's simple: they should happen when operations are performed on checked integers. So if you do an operation on two int it will not be checked, if one is int and the other cint, then the type system will decide to call the checked operator, and so there will be a check. With overloading on the result type (which is not available at the moment), we could also say that using the result of a int,int operation as a cint should be a checked operation.
-- DanielBonniot
Many folk seem to forget that JVM does modulo arithmetic - there's a large community for whom this just isn't a problem - whatever I might think. And this isn't something novel that people would be excited about.
Maybe work should concentrate on really ordinary things that will be used a lot (Visibility? Properties?) and really extraordinary things that are made possible by the type system (XML support? Variance sugared syntax?).
-- IsaacGouy - 23 Dec 2003
I agree with these priorities. On the other hand, I believe that the way to accelerate the development of Nice also lies in doing more things in parallel. This means that while the people familiar with the core compiler (Arjan and me) look at features like constructors and properties, it only helps if others are working on tools, libraries, ... In particular, I believe that checked arithmetics is not a hard feature, and that it can be implemented at 90% as user libraries. If somebody provides the checked operations on ints, it should be a matter of say 30 minutes to add the cint type to the compiler. So it won't really slow us down :-) -- DanielBonniot - 24 Dec 2003
With the addition of type declarations(which could allow declaring subtypes of primitives too) CheckedIntegerArithmetic can be a library thing for 100% -- ArjanB
"type declarations" Is there a proposal? -- IsaacGouy - 24 Dec 2003
A cint type seems too low-level since it's tied to JVM details rather than the problem space. I think it would be more useful and more high-level to have checked integer types with a user-specified range. For example, integers in the range [1...100]. That way you find out earlier about incorrect values and the compiler can choose an int, long, or BigInteger? implementation as appropriate. Operations done with these integers would use intermediate values as large as necessary to prevent overflow and do the runtime check when assigning to a non-temporary. (Of course, it's more work for the compiler.) -- BrianSlesinsky - 30 Dec 2003
You may want to check out Javolution and JScience for some nifty ideas.
Particularly LargeInteger? (in JScience), and some other stuff in there.
I think the idea of using a long for multiplication is good (i.e. result = (long)a * b), then you can do a simple overflow = (result != (int)result). For addition/subtraction, basically, you just have to check that the sign bit (i.e. MSB) is the same as one of the operators. So for result = a + b, overflow = ( (result >> 31 != a >> 31) && (result >> 31 != b >> 31) ) -- MotiN - 17 Jan 2005
I'm looking ahead to when invariants are added, and the crossover with NiceConstructors. Vague thoughts only, feel free to edit.
There's an Eiffel hack to allow different sets of invariants in different conditions: express your invariants as
The trouble with using this for constructors is you have to list what parts of the invariant each method really depends on in each method, which defeats the purpose of an invariant.
I wondered about a distinction between methods that preserve the invariant but don't require it, which could be freely called both within constructors and outside them, and ones that both preserve and require. Note that this is a different situation again to constructors, which do not require the invariant but guarantee to make it satisfied: a preserving-only method would not necessarily make it satisfied if it wasn't when the method was called. The problem with this arises when the preserve-only method is called directly after some buggy method has broken the invariant. The preserve-only method doesn't do the proper checking, because it's happy to accept a broken invariant at method start.
My suggestion is a general condition in_constructor which is set true at the start of each constructor and false at its end. Preserve-only methods guarantee to check the invariant
at start and end if in_constructor is false,
and at the end if
in_constructor is true
and the invariant is satisfied at method start.
For this to work with nested constructor calls I guess constructors only set it to false at end if it was false before they set it at start, or something.
Anyway, this is entirely off-the-cuff uninformed thinking out loud, with no idea how implementation-practical the idea is.
-- SamsonDeJ - 15 May 2003
It's a good idea to start with high-level thoughts about the design. If a feature is well designed, it is usually easier to implement it, and of course it makes the language more beautiful.
Could you comment on the proposal in NiceConstructors? If we can separate the construction from the initialization, then there is the possibility to get rid of this invariant problem altogether.
By the way, I appreciate to have somebody fluent in Eiffel to participate in here. It can give a different perspective, and contribute to a richer discussion. Knowing the existing is a must to improve over it.
-- DanielBonniot - 16 May 2003
I would also appreciate hearing from someone who knows Eiffel. Unfortunately it's not me - I know a very little about Eiffel, and picked the above off a webpage. -- SamsonDeJ - 16 May 2003
The compiler can be called from JVM languages (Nice, Java, ...) by using a high level API, instead of starting a different OS process (application). This is used by the command line compiler, but also by IDEs (EclipsePlugin at the moment), and might be useful in other contexts.
The main file defining the CompilationAPI is nice.tools.compiler interface.nice.
One key argument to the compile method is a bossa.modules.Compilation object, which is used to configure the compilation (what package to compile, where to put the generated jar, ...).
The Compilation also contains a bossa.modules.CompilationListener, which receives messages that report the progress of the compilation. Here is its interface:
public interface CompilationListener
{
void error (Location location, String message);
void warning(Location location, String message);
/** A bug occured in the compiler.
@param url the adress where a bug report should be submitted.
*/
void bug(String stackTrace, String url);
/** Reports the progress of compilation.
phase can be: parsing, type-checking, generating code, ...
the package can be null if the phase applies to the whole program
(testing dispatch, creating the archive, compiling to native code, ...).
*/
void progress(String packageName, String phase);
/** Gives an approximation of how much of the compilation has been completed.
@param proportion the current progress
(0.0 = just started, 1.0 = complete).
*/
void progress(float proportion);
}
I'm trying to summarize my ideas on constructors, based on the discussions we had.
I think we should separate two aspects: creation of new instances, and initialization of new instances. The creation aspect is what interests clients of a class: they want to be able to construct instances that have a certain property, which is described by the arguments they pass. For the author of a class, there are cases where it is possible to return an existing object instead of creating a new one: if the class is immutable, and an object with the correct property already exists. For instance, consider:
class Point
{
final double x;
final double y;
}
For a client, a natural way to construct a point would be new Point(x: ..., y: ...);. The author of the class might anticipate or know by analysis that many instances of Point actually have the same coordinates, so he could decide to improve the efficiency by sharing their representation. He should be able to do so without chaning the API, so the clients can still use new Point(x: ..., y: ...);. So he should be able to write a "creation method", for instance:
let Point origin = new Point(x: 0, y: 0);
Point new Point(double x, double y)
{
if (x == 0 && y == 0)
return origin;
else
return new Point(x: x, y: y);
}
The problem with this is the inside call to new Point(...). It will be a recursive call, and so will never finish (similarly, the value for origin would execute the creation method, which would try to read origin, which is not set yet). One solution would be to treat specially new inside a "creation method". However, this could get messy, and I would prefer a clean solution without such hacks. One idea is to give a special syntax for calling a "real constructor", ignoring creation methods with the same name. For instance:
let Point origin = Point.make(x: 0, y: 0);
Point new Point(double x, double y)
{
if (x == 0 && y == 0)
return origin;
else
return Point.make(x: x, y: y);
}
With this, we can treat creation methods as normal methods (except for parsing their name). "new Point" is a normal method, so we can define it in a Nicer way:
let Point origin = Point.make(x: 0, y: 0);
Point new Point(double x, double y) = Point.make(x: x, y: y);
new Point(0,0) = origin;
One could be worried that allowing Point.make is exposing a detail about a class: if a client uses it and the class changes so that there is no such "real constructor", then the client will break. However, I think this is fixable. First, the author of the class can provide a CustomConstructor?, which is also reachable with the Point.make syntax. Second, it should be possible to use visibility. Should Point.make be only package visible, not public? This part needs some more thought.
Another good aspect of the make syntax is that it suggest a syntax for CustomConstructors that differentiates them from creation methods:
Point.make(double angle, double distance) = new Point(x: ..., y: ...);
It should even be possible to define Point.make as a normal method:
// Optimization for angle = 0
Point.make(0, distance) = new Point(x: distance, y: 0);
One possible improvement is to allow creation method implementations without a declaration. In that case, the declaration would be taken by looking at matching constructors (custom or not). This would allow the following:
let Point origin = Point.make(x: 0, y: 0);
new Point(0,0) = origin;
This makes some sense because, from the clients point of view, new Point exists even without a creation method declaration, and it defaults to the custructor. Would there be any drawback with this additional feature?
-- DanielBonniot - 18 Dec 2003
It's just struck me that what we're talking about here is very similar to Dylan's way of handling this problem. I'll include a reference here for comparison. Nice is very similar to Dylan in many ways, so it's not a bad idea to see what Dylan does when we have questions about what Nice should do.
Instance Creation and Initialization
-- BrynKeller - 18 Dec 2003
A different idea is to use super inside a creation method to refer to the real constructor. This would give the following version for the example:
class Point { double x; double y; }
let Point origin = new Point(x: 0, y: 0);
new Point(0,0) = origin || super;
The syntax new Point(0,0) = origin || super is not intuitive.
Not sure if you are familiar with ||, but this is not specific to constructors. The equivalent code in a more traditional notation would be:
new Point(0,0) {
if (origin == null)
return origim;
else
return super;
}
I didn't know this because it isn't documented. Now I understand what you meant.
From what I understand you want to allow the user to overload the new operator on a class-by-class basis. I think that is very confusing. When the expression new Point(...) is encountered we expect to have a new object allocated. In particular, it is always the case that
new Point(0,0) != new Point(0,0)
The idea of object identity becomes unclear because it can't be described in terms of new. Imagine the craziness that would ensue for something like IdentityHashMap<Point,T>.
Yes, you get less control over when a new object is created or not. But that's the idea: most of the time, you don't need to know, and giving you this control causes lots of problems, which is why it is advised to use factory methods. When you call a factory method, you don't know if a new object is created or not either.Could you give an example of craziness that would ensue with IdentityHashMap<Point,T>?
I will concede that it will not cause problems as long as the documentation is clear that the new operator does not guarentee uniqueness w.r.t. object identity (System.identityHashCode(Object)).
The example above can be written using a factory method, so I don't see what kind of expressivity you are gaining:
let Point origin = new Point(x: 0, y: 0);
Point makePoint(double x, double y) = new Point(x: x, y: y);
makePoint(0.0, 0.0) = origin;
What we gain is that you don't need to defensively write all the code of a factory, just in case it turns out later. Either you do it in all cases, and a large proportion will be wasted effort (even with a good IDE, it's still cluttering the code), or you don't, and then you are stuck since clients started using new YourClass?, and you cannot make the changes that you need without breaking this API. I think these are the same benefits as for properties, where there is a known workaround (getters and setters), but it's a pain to have to do it by hand when the compiler could do it for you.
I disagree. If you start out writing code with the default constructors and then change the fields in the class, you have to remember to add an explicit custom constructor with the old interface to maintain binary compatibility. This is easy to forget to do and the compiler won't be able to help you find this error. For this reason, I think that implicit constructors are bad when used across package boundaries and that explicit constructors should always be used across package boundaries; if VisibilityModifiers were implimented than I would propose that implicit constructors should have package private access. (A more general rule would be that I think that a package's public interface should be explicitly defined in the code.) Once you have written an explicit constructor it doesn't really matter so much which syntax you choose (factory method or constructor), as far as I can tell.
-- BrianSmith - 29 Jan 2004
Also currently we have new Point(...) as equivalent to Point.getClass().getConstructor(...).newInstance(...). But, with the new system, we have don't have this symmetry:
True, but this is a rather lowlevel property, isn't it. How often would it be problematic? How often would the new system safe you work or let you improve your code without breaking the API?
I am just saying that it makes the semantics hard to explain because you can't piggyback on the JLS/JVM specs anymore in these areas. So, you have to write new documentation for the semantics of object creation/identity and for part of the reflection API.
-- BrianSmith - 29 Jan 2004
It would still be possible to offer another construct for surely creating a new instance, provided it's allowed by the class. That can be discussed. Basically, this would make new ... the equivalent of Java factory methods, and the other one the equivalent of Java's new. Since the latter is much rarer in my opinion, we would gain a lot by making it easier.
abstract class A { int getValue();
// more methods
}
class Zero extends A { int getValue() = 0;
// specialze other methods for ZERO
}
class Other extends A { final int value;
int getValue() = value;
// generic implementations for non-ZERO values
}
let Zero ZERO = new Zero();
Then we could have either:
new A(int value) = new Other(value: value);
new A(0) = ZERO;
or:
A makeA(int value) = new Other(value: value);
makeA(0) = ZERO;
I find the second version to be much easier to understand and it can already be done without any language changes. When VisibilityModifiers are implemented then the package author can mark all constructors private and then provide public factory methods like makePoint and makeA. I think this is a good practice anyway.
It's good practice in a language that provides no such feature as we are discussing. The whole point is to make this extra work unecessary.Isn't the second easier to understand because you are used to it, while the first is a new proposal? Doesn't makeA look like a hack?
More and more Java API's are designed so that the API is (almost) purely defined by interfaces, so that factories must be used anyway. That is the style I prefer so I don't find makeA to look like a hack to me. Anyway, I think the argument could be turned around as "Isn't using constructors easier to understand because you are used to it?"
class ColoredPoint { int color; }
new ColoredPoint(int color) = new ColoredPoint(color:color, x: 0.0, y:0.0);
The coder has specialized Point(0.0,0.0) but this specialization obviously cannot be used by the subclass. So, then you have two sets of rules for deciding what constructor implementation gets chosen (one for direct invocation, one for subclass invocation).
-- BrianSmith - 26 Jan 2004
Yes, this is the distinction between CustomConstructors and OverloadedConstructors?. new Point(0.0, 0.0) is an overloaded constructor, and you cannot use it to construct subclasses, in the same way that in Java, in a constructor you can use a parent constructor but not a parent factory method. Maybe we should say "factory method" instead of OverloadedConstructor??
-- DanielBonniot - 27 Jan 2004
I think that there should be a syntactical distinction between OverloadedConstructors and CustomConstructors. They are different concepts and I think users will get confused by this:
class Foo { } { ... }
....
new Foo(Number n, Number n) { ... }
...
new Foo(Double d, Double d) { ... }
Is the second constructor above an overloaded constructor, a custom constructor, or a specialization (dispatch-wise) of the first constructor. Maybe you can tell just by reading the code. But, imagine that there are 50 lines of code where the ... lines are. Or, imagine now that all three elements are defined in seperate packages. Now how can you tell?
-- BrianSmith - 29 Jan 2004
Agreed. If I remember, the difference was supposed to come from the presence or absence of a return type (so your first constructor should have one, if it's supposed to be an overloaded constructor, no?). But it's true that if you can specialize an overloaded constructor (which makes sense), then there is syntactic ambiguity.
Any proposal?
-- DanielBonniot - 30 Jan 2004
My proposal is to avoid implementing this feature. :) Or, at least, do not overload the constructor syntax for this purpose. If you do so, then you will have to go to great lengths to document a tedious source-code -> bytecode mapping and/or the binary compatibility rules.
How do you deal with binary compatibility for constructing an instance of a class defined in another package? It seems like inter-package object construction must always go through a "thunk" factory method:
package A, version 1.0:
class Point
{
final double x;
final double y;
}
package A, version 2.0:
class Point
{
final double x;
final double y;
}
Point new Point(double x, double y)
{
if (x == 0 && y == 0)
return origin;
else
return new Point(x: x, y: y);
}
package B:
import A;
let Point myOrigin = new Point(x:0, y:0);
It seems like you can compile package B as a normal object instantiation (using the JVM's new instruction) against version 1.0 of package A. But, you cannot compile it that way against version 2.0 of package A. I believe that package B should not have to be recompiled due to this change in the implementation of package A, since package A's public interface is the same; that is, I should be able to compile package B against version 1.0 of A and then run package B's code against the compiled version of version 2.0 of A. So, it seems like you have to generate a thunk method equivelent to makePoint for both versions of package A.
I think it would be better to simply discourage people from directly constructing instances of classes from external packages by making all constructors protected by default, and requiring people to add code like:
// I can implement optimizations like the above in my
// factory method since the Point constructor is not public.
public Point makePoint(int x, int y) = new Point(x:x,y:y);
or:
// I cannot implement optimizations like the above since
// I am directly exposing a constructor to external packages.
public new Point(int x, int y);
This makes the language simpler to learn and makes the binary compatibility rules much easier to understand.
-- BrianSmith - 01 Feb 2004
So what you are advocating is exactly the same as Java, right?
It is slightly different. In Java, public classes have an implicit public constructor by default. I am proposing that any implicit constructors be package-private in Nice.
Again, the problem with this approach is that you allow, and even make the "default" since it is easier, to write inflexible code, whose implementation you cannot change afterwards. This is the same problem as methods in C++, which are not virtual unless you explicitely say so, or with fields. So people come up with rules like "always write factory methods" and "always write getters and setters", which indeed avoid the problem, but put the burden on the programmer.
I don't think my suggestion makes it easier to write inflexible code--in fact, I think it does exactly the opposite. Currently, there is no way to enforce that a factory method must be used to construct an instance of your class since the constructors are always public. Therefore, changes to the implementation are restricted because this public constructor must be supported. In my "proposal," the choice of factory method vs. constructor more explicit: if you don't write a public factory method or a public constructor then your class cannot be instantiated from code outside the package at all.
Right, you proposal is an improvement over the current situation in Nice. But it is more inflexible than my proposal because if you don't start with a factory method, you will never able to switch to one without breaking the public API. -- DanielBonniot - 08 Feb 2004
This isn't an issue any more since all object creations will go through factory methods now, right? There remains the issue of whether the factory methods and constructors should be exposed by default but that can be dealt with in VisibilityModifiers.
I don't think using Nice will be more complex with this proposal. It will become easier, because you don't need to worry or bother about factory methods, but if you realize you need to override the default behaviour, you are sure to be able to do it.
It's true that the implementation becomes a bit more complex, but don't worry, I am the one doing it, and I think it's worth the above benefits. About binary compatibility, your solution is correct, and anyway this is an implementation detail (I don't think users should even need to know about binary compatibility rules. Computing power for recompilation is cheap, compilers can be made clever if needed, while user brain's attention is better directed at the design of his program). At the moment, the compiler doesn't check if the public API changed or not, it always recompiles if an imported package changed, which is safe. We might optimize this behaviour in the future, but at the moment I consider more important to "get the language right", so I focus on that. -- DanielBonniot - 01 Feb 2004
You are assuming that package B can be recompiled whenever package A changes, right? But, this is not the case when package A and package B are written by different people, for example when package A is a reusable library packaged in a JAR file and package B is an application that depends on it.
But a new version of A might change its public API. So it is normal for the author of B to recompile his application if he upgrades to a new version of A. It's true that you might be a user of B, you don't have B's source code, and you have a new version of A which does not change its public API but is faster, or safer, for instance. In that case, yes, it's better if you can use the new version of A without recompiling B. But that's a quite peculiar situation. And you should keep in mind that even if A's public interface does not change, it's behaviour/semantics might change, so there is no automatic way the compiler can know if it's safe or not to upgrade A with an old B. But anyway, as you showed, it is possible to keep binary compatibility, it's just a question of implementation, so I would rather that we don't complicate the discussion about the desired definition of the language with this.
I agree, the visibility issue can be discussed in VisibilityModifiers. The other point I was trying to make is that I think that object creation expressions (currently new Point(x:1,y:2), or Point(x:1,y:2) using the syntax you suggested below) should be defined to always invoke factory methods, for the same reasons that motivated the PropertySyntax proposal.
I agree, sorry if that was not clear. -- DanielBonniot
In NiceConstructors, you said "creation = construction + initialization." But, let's extended this to "creation = allocation + construction + initialization" or "new X(...) = allocate X(...) + initialize X()," and by default "allocate X(...) = construct X(...)." Then you could support something like:
class Point
{
final double x;
final double y;
}
// custom factory method
allocate Point(double x, double y)
{
if (x == 0 && y == 0)
return origin;
else
return construct Point(x: x, y: y);
}
// overloaded constructor
construct Point(double distance, double angle) {
construct(x: blah, y: blah);
// instead of "this(x: blah, y: blah);"
}
// class instance initializer for Point
initialize Point {
...
}
Then you would not overloading the new keyword for three different concepts any longer and you avoid ambiguity between overloaded constructors and factory methods.
-- BrianSmith - 02 Feb 2004
Note that you used "overloaded constructor" for what we called custom constructor until now.
Yes, that's a clear distinction of the different aspects, which is important to have in mind. The question is, do we want to use this syntax? It's clear, but it's also very baroque/unusual. What is sure is that we should distinguish the different aspects.
One idea would be to reserve new for constructor declarations, and only the class name for factory method declarations. The idea is that a constructor is involved in the creation of a new instance, while factory methods might return an existing instance, null, ...
This would also avoid the ambiguity problem, without introducing those new keywords.
On the usage side, we do not want to distinguish, because the whole idea is that you can evolve the implementation without breaking the API.
We could also support Point(x: 1, y: 2) for calling constructors and factory methods (that is, without the new keyword), since that is a lighter syntax, and puts less stress on the creation of a new instance, which might not be the case anyway. But that's a change that can be done independently of the rest of the proposal.
-- DanielBonniot - 02 Feb 2004
That to make a lot of sense to me; in fact it seems to be the same as my original proposal except that the factory method is automatically generated if an explicit one is not provided. Just to make sure I understand: The expression Point(x: 1, y: 2) would always call a factory method. If a factory method has not been explicitly defined, then there is a compiler-generated one of the form Point(double x, double y) = new Point(x: x, y: y);. What would happen with the expression new Point(x:1, y:2)? Would it be disallowed (allowable only in factory methods from class Point), call a factory method, or directly call a constructor? Personally, I find the idea of new Point(x:1,y:2) expressions not creating a new instance to be confusing.
What would happen if there were multiple constructors defined for the class; would there be an auto-generated factory method for each one?
What would the exact syntax of a factory method be?:
// same syntax as other methods.
Point Point(double x, double y) { ... }
or
// assume the return type is Point.
Point(double x, double y) { ... }
The Point(x: 1, y: 2) syntax also matches the int('c') syntax already used for primitive types.
class Point
{
final double x;
final double y;
}
// custom factory method
Point(double x, double y)
{
if (x == 0 && y == 0)
return origin;
else
return new Point(x: x, y: y);
}
// overloaded/custom constructor
new Point(double distance, double angle) {
this(x: blah, y: blah);
}
// class instance initializer for Point.
// Perhaps the initializer should always
// appear in the class body so "initialize"
// doesn't become a keyword?
initialize Point {
...
}
The long term goal could be to encourage the use of Point(...) instead of new Point(...) on the client side. But I think for a longish transition period both should be accepted. It would simply be breaking too much assumptions otherwise.
Yes, for each constructor for a given class, there will be a way to create a new instance with those parameters, so that's like an automatically generated factory method. And I think the syntax for factory methods should be just like normal methods. No need for specific treatmeant.
-- DanielBonniot - 03 Feb 2004
OK, I hope that we are getting to a conclusion. CustomConstructors are defined with the new Point(...) { ... } syntax. Later on, we will implement factory methods, defined with Point(...) { ... } syntax. At the call site, one always call a factory method (but that can be the one implicitely defined by a CustomConstructor?. We can also allow the syntax Point(...) for creating a Point, which will address the confusion that Bryn signaled, namely that new Point(...) might not create a new instance.
If there's agreement on this, then I'll release 0.9.6 soon with the current implementation of CustomConstructors, since they seem to fit with the global proposal (that is, source code working with the 0.9.6 release will work the same when the global proposal is implemented).
-- DanielBonniot - 08 Feb 2004
Sure, things make sense as far as I can tell. But, if you are going to recommend that people use Point(...) instead of new Point(...) then I recommend to make that change before you do any more releases. This includes updating the documentation and the examples. Also, are you planning to flag the new Point(...) syntax as deprecated? It seems confusing to have two syntaxes for the same thing, and then also have new Point(...) do three different things depending on the context.
-- BrianSmith - 08 Feb 2004
On the one hand, I agree that the earlier such new syntax is introduced the better. On the other hand, 0.9.6 has already been delayed a lot, because I did not want to release a feature that we were not quite sure was yet in its probable final version. Furthermore, it might be good to have the new creation syntax some time in development version. And it's purpose is especially evident with factory methods, which are not there yet either. We'll see if that can be implemented soon, but I want 0.9.6 released soon (it has several important bug fixes). In any case, we can only start changing documentation after a released version implements a feature.
-- DanielBonniot - 09 Feb 2004
Construction is the process of creating a new object whose fields have some meaningful value. Currently, only the automaticly generated default constructor does that. It takes the value of each field as a named parameter.
Basically, a custom constructor could be defined as a normal method:
class Point { double x; double y; }
Point createPoint(double angle, double distance) = new Point(x: ..., y: ...);
The problem with this solution is that a subclass can not make use of the custom constructor for its parent, since it includes the creation of the object itself, instance of the parent class. Instead, we want the a custom constructors takes the new instance (this), and sets its fields. However, this cannot be done with a normal method, since we do not want to give a way to create an uninitialized instance, as this would be unsafe (like accessing this in a Java constructor is unsafe).
Therefore, we propose to add the concept of a custom constructor. Its declaration is similar to a method default implementation, except that it has no return type, and its name is the class C being constructed, preceded by new. The parameter list is arbitrary, as is the body, except the last instruction: it must be a call of the form new(...), targetting either the automatic constructor, or another custom constructor. This guarantees that all fields are set to a correct value. The body before this last instruction can be used to compute the arguments of this last call. this is not accessible anywhere in the custom constructor, since it is not properly initialized yet.
class Point { double x; double y; }
new Point(double angle, double distance) { this(x: ..., y: ...); }
This allows to create a new point with new Point(angle: ..., distance: ...). Furthermore, a subclass can be constructed by reusing this custom constructor:
class ColoredPoint extends Point { String color; }
var ColoredPoint redOrigin = new ColoredPoint(angle: 0, distance: 0, color: "red");
A custom constructor can also reuse the parent custom constructor indirectly, through the default constructor of the child class:
Calling new C(...) in the constructor seems redundant since C always matches the constructor name, and misleading since we might actually be constructing a subclass. Maybe a syntax like this would work better?
Agreed, new C(...) is not a good syntax. We are thinking about using this(...) , but new(...) is an option too.I updated the syntax above, using this(...). Which one do you prefer: this or new? DanielBonniotI think this(...) would be too close to Java since the rules are different. (The constructor goes at the end, and this(...) suggests super(...) which doesn't make sense for Nice. But I'll defer to folks who are actually using the language. - BrianSlesinsky
I like being able to do things before calling the parent constructor, with the caveat that "this" is unavailable. But suppose you wanted to keep a hashmap of all objects of a certain type? In Java you'd add "this" to the hashmap in the constructor, which is unsafe but useful. Perhaps there should be a second pass that's started by automatically calling an init() method (if present) after all the constructors have finished?
The generated Java code would be something like:
this(...);
if(no subclass or subclass is not written in Nice) { this.init(); }
-- BrianSlesinsky - 13 Dec 2003
This is already possible using initializers, though the subclass check isn't implemented yet.
let Set<Foo> allFoos = new HashSet();
class Foo
{
{
allFoos.add(this);
}
}
-- ArjanB - 13 Dec 2003
Do you want to require that custom constructors should be defined within the class body? I think this is a good requirement because it distinguishes them more from methods. Also, they have access to "this" which should only be available from within the class body. Finally, constructors must be declared in the same package as the class anyway (package b cannot define a constructor for a.A).
There are no such restrictions: CCs can be defined outside classes, and in any package. On the other hand, they don't have access to this, as specified above (accessing this inside a constructor is unsound, since the instance is not yet fully constructed).
Okay, I misread "this is not accessible in the whole custom constructor" to mean "this is not accessible EVERYWHERE in the constructor" instead of "this is not accessible ANYWHERE in the custom constructor."
OK, my mistake, the grammar was imperfect :-)
I restore the sentence in its new form, as I assume I got lost by accident.
How do custom constructors relate to SuperCall? It seems they are similar. I wonder if parent constructor invocation can be defined in terms of SuperCall? In Java they use super for both. Is there a reason not to use super in Nice too?
The idea is that every CC must (possibly indirectly) call a default constructor. This ensures that all fields are set. For every parent constructor (custom or default) there is a corresponding default constructor, which calls the parent and set the declared fields of the current class. So this is how you call super constructors. Since you don't call them directly, it would be illogical to use the super keyword.
Thanks, this explanation is what really helped me understand the proposal.
It is not clear from the examples if we are allowed to remove the labels for the constructor parameters, but I assume so since you can't use labels if you are subclassing a Java class anyway:
Do the same restrictions apply as in SuperCall? For example, is the following legal?
class A { A(int f) { myF = f; }
int myF;
}
class B extends A { B(int f) { new(myF: f / 2); } // passing different value for f
// is not allowed in SuperCall
Yes, there is no such restriction for CCs, this is legal.
The parameter list is arbitrary, as is the body, except the last instruction: it must be a call of the form new(...), targetting either the automatic constructor, or another custom constructor. This guarantees that all fields are set to a correct value.
I think this would be more clearly (and correctly) stated as:
The parameter list is arbitrary. If the class has a superclass, then the first instruction of the body must be a call of the form new(...) targetting either another constructor in the class being defined, or a constructor in the superclass of the class being defined. Any code may follow the new(...) call as in a method implementation.
No, we differ from Java here. We can indeed have instructions before the call to the other constructor. Those cannot access this of course, as it would be unsafe. In the current implementation, you cannot write anything after the call, although one could imagine an extension where it would be possible, provided any execution goes through calling another constructor once and only once. Currently, you would use an initializer if you need to do anything at the end.
Furthermore, require that the constructor must assign a value to every field that does not have a default value defined for it in the class's body; i.e. there are no implicit defaults (like in Java where an int defaults to zero if no value is assigned to it). Or, would you like to say that if a constructor parameter has the same name as a field, then that parameter's value is automatically assigned to the field?
This request is not explicitely needed, it's a consequence of the call to another constructor.
State that if a custom constructor is defined then the automatic constructor is not generated (true?).
At the moment the default constructor is still generated. It is still needed, as it is the one that really asssigns the fields. Later, we will introduce a way to make the default constructor private only.
Explain how self-calls dispatch in constructors, if different than dispatch from method bodies.
There is no self-call, since this is not available.
Explain the order in which initialization blocks and constructor bodies are run. Is it even necessary to have initialization blocks anymore?
-- BrianSmith - 18 Jan 2004
Initialization is performed after the execution of constructors. The chaining of constructors is explicit, so there is no special rule. -- DanielBonniot
How useful it is to have code execute before the constructor call? In Java and C++, I have only needed to do this a few times and in all such cases I was able to define simple functions that did the prior computations for me. How much expressivity would be lost if constructor bodies were limited to only a single constructor call, or a series of let expressions and a single constructor call?
Why do you want to restrict what can be done in CustomConstructors before the single constructor call? I see no reason for that and I'm sure someone find useful things you can't do with only let expressions.
It makes the concept simpler and easier to understand. The emperical evidence from Java suggests that making this simplification is not going to cause problems. Assume that custom constructors can only contain a single constructor invocation (no "let"). Then you can write every custom constructor as an equivalence:
class Point { double x; double y; }
Point(double angle, double distance) = Point(x: angleOf(x,y), y: distanceOf(x,y));
or new Point(double angle, double distance) = this(x: angleOf(x,y), y: distanceOf(x,y));
or new Point(double angle, double distance) = new Point(x: angleOf(x,y), y: distanceOf(x,y));
You don't have to explain anything like whether or not self is available in constructor "bodies" and whether code is allowed before/after constructor invocations because constructors don't have bodies any more. It makes it clear that a custom constructor is just a an alternative for another (custom) constructor. It also emphasizes that constructors are for initializing a fields values, if you are planning to follow the creation = construction + initialization definition Daniel gave in NiceConstructors.
-- BrianSmith - 20 Jan 2004
I often resent this limitation in Java. True you can always work-around it by using help methods, but I think it just clutters the code. Moreover, often you will be able to compute several values at once, but if you don't even have let, you will need to split the computation, once for each argument of the call.
It's true this would simplify the definition, but I'm afraid it is too restrictive. Besides, I expect that the question would pop-up from time to time: "why cannot I do computations before the call?", so we would need to add explanations anyway.
Fair enough. The reason I suggested the possibility of only allowing let was specifically to address the problem you mentioned (computing many values at once) while still having the syntax suggest that the constructor is only for field initialization. But then I realized that even the use of let would almost never be necessary. There is no way to enforce or even suggest (in the language syntax) that any code written in the constructor is relevant to field initialization, if constructs other than constructor calls are allowed in constructor bodies. Therefore, it doesn't seem to make much sense to forbid statements after the constructor call but allow them before. -- BrianSmith - 20 Jan 2004
The "new Point" syntax for defining constructors looks a lot like the syntax used for defining and instantiating anonymous classes (in Java anyway):
class Point { double x; double y; }
new Point(double angle, double distance) { ... } // define Nice constructor
new Point(a, d) { ...} // anon. class instantiation in Java
new Point(angle:a,distance:d) { ... } // presumably how anonymous classes
// would be instantiated in Nice.
When I see the keyword "new" I naturally think an instance is being constructed. Maybe the name of a constructor should just be the name of the class (without "new") like in Java, C#, C++?:
class Point { double x; double y; }
Point(double angle, double distance) { this(x: ..., y: ...); } // no "new"
Nice doesn't have anonymous classes but the syntax of CustomConstructors is a topic of discussion.
Three choices are not decided definitely at the moment: new(...) or this(...) as constructor call in a CC, requiring "new" or not before the definition of a CC and where CC can be put: only inside class or outside too.
I think this(...) is not a good idea since it seems to imply that self-calls are allowed. Also, I don't like new(...) because I prefer to think of new as the allocate/construct/initialize operator. I think the Point(...) = Point(...) syntax I suggested above is clearest.
-- BrianSmith - 20 Jan 2004
Yes, Point(...) has its merits. The only problem I see is that then you could be tempted to write such call with a different class, while that would not make sense. So it has redundant information.
At the moment I prefer this(...). I don't really see it as confusing with self-calls, which are this.foo(...). An advantage is that this(...) really has the same semantics as in Java: "call a constructor from the same class". So it makes sense to use the same syntax.
I agree that if you are going to allow code before/after the constructor call then this(...) makes the most sense out of any points considered to this point. -- BrianSmith - 20 Jan 2004
By the way, I think that this article is a good description of why self-call in constructors causes problems in Java and C#'s way of "fixing" the problem: http://www.jot.fm/issues/issue_2002_11/article4.
-- BrianSmith - 19 Jan 2004
Interesting article but C# does only solve one of the problem Java has with constructors. As far as I understand C# doesn't solve the problem as shown in the Parent-Child example at 2/3rd of NiceConstructors page.
-- ArjanB - 20 Jan 2004
Agreed, the semantics in C# is not satisfactory either. I think Brian agrees, and was saying exactly that. But the article seems slanted towards C# and does not make this point.
Sure, I know that C#'s solution is not perfectly safe. It disallows self calls in field initialization expressions but allows them in constructors. -- BrianSmith - 20 Jan 2004
What happens when you define a constructor that has a signature matching the default constructor?:
class Point { double x; double y; }
new Point(double x, double y)
{ this(x: ..., y: ...); }
Presumably, this would be helpful in order to add exception declarations, preconditions, postconditions, etc. to the default constructor. Yet, it is not clear how the fields of the class would be initialized because a this(...) call would be recursive.The specification should state that "loops" in constructor calls are not allowed (e.g. a constructor constructor A is defined in terms of constructor B, while constructor B is defined (perhaps indirectly) in terms of constructor A, as in Java. And the Nice compiler needs to check for this, of course. -- BrianSmith - 20 Jan 2004
In case of a CC with the same signature as a default constructor, the current implementation just rejects the call to this(...) as ambiguous.
I agree loops should be detected. Amusingly, the current implementation fails at compile time with a StackOverflowError in such situation. So it seems that the check is there (by chance). OK, the error message could be improved :-)
DanielBonniot - 22 Jan 2004
When I wrote "What happens when..." I meant "what is designed to happen when...." In other words, is replacing the default constructor supposed to be allowed? If so, then how do the fields get initialized (using what syntax)? Using this(...) seems confusing in this situation because you are "calling" the constructor that you are replacing.
I did not really think about it before. A priori it seems to me that this situation could be forbidden. Would that be problematic? -- DanielBonniot
Consider:
class X { int n; int m }
new X(int n, int m) // forbidden because it matches the
requires foo(n,m) { // signature of the default implicit constructor
this(n:n,m:m); // confusing: looks (infinitively) recursive
}
new X(Integer n, Integer m)
requires foo(intValue(n), intValue(m)) {
this(n:invValue(n), m:invValue(m));
}
Why should the first constructor be rejected and the second one be accepted? I don't think the first one should be rejected on the grounds that the syntax required to support it is awkward.
It not just that the first one looks recursive, it is ambiguous, using the normal rules of the language. We might create an exception for this case (how would it be phrased precisely?), but before complexifying the language I would like to be convinced this is worth it.
I don't buy the argument about the second version. Integer is a completely different type than int, so there is no ambiguity. (OK, it happens that there is a special conversion in the bytecode, but I don't see how this changes the general point).
-- DanielBonniot - 30 Jan 2004
I put the second one there just to have something similar to compare the first one to. I assume that no matter what you decide the second one would be accepted. -- BrianSmith - 30 Jan 2004
OK. I now think it indeed makes sense to forbid the ambiguous situation. The idea of custom constructors is to offer alternative ways to construct an instance of a class. For instance, even though Points might happen to be implemented by a couple x,y, it is as valid to specify what instance you want with the couple distance,angle, so you can write a custom constructor with that signature. On the other hand, a custom constructor with signature x,y does not make sense, because such way to specify the instance already exists. If you wanted a different behaviour than the default constructor, it would be just confusing (like new Point(x,y) = this(x: y, y: x) !). And custom constructors are not the place to put initialization code, as discussed above, so that's not a reason to want a custom constructor either. -- DanielBonniot - 01 Feb 2004
Then how would one validate the initial values given for the fields of the instance? In the initializer? -- BrianSmith - 02 Feb 2004
When typing is not precise enough, you can specify the acceptable initial values in a requires clause. It would also be possible to use the initializer, but a contract seems more adapted, because it is part of the signature of the constructor (will be printed by NiceDoc, for instance). -- DanielBonniot
But, how would you add a requires clause to the default constructor? That was what motivated my example above. -- BrianSmith - 04 Feb 2004
I see. Since the default constructor corresponds to the internal representation of the class, the requirements could be put in the invariant of the class. -- DanielBonniot
It isn't always possible. Imagine that the precondition for the constructor is "an instance with the given (x,y) coordinates does not already exist in some pool." Then the initializer will add the new instance to the pool. In this case, the invariant for the class would be "an instance with the given (x,y) coordinates does exist in some pool." But, obviously the invariant can't be both at the same time. In other words, the constructor's precondition is only required to hold before construction, but not necessarily every point in time afterwards like the invariant. -- BrianSmith - 04 Feb 2004
Right. For this kind of stateful requirement, you could indeed use an assertion in an initializer. -- DanielBonniot
Download http://nice.sourceforge.net/nice.jar, then replace the nice.jar of your current version of Nice with it.
This file is basically CVS updated a few times a day, but only put there if all normal tests are passed, it can bootstrap itself and
it can run a few more apps.
-- DanielBonniot - 28 Oct 2002, raboof - 06 Apr 2005
This is the ability for a class to add members to their class at runtime. Actually, that's not what I'm proposing. I really want classes to be able to make it appear that they have other members at runtime. This could be implemented in (at least) two ways.
One way would be to allow programmers to override the `.` operator. This would be very powerful, especially if the compiler would allow other things besides simple names after the dot, such as ints, floats, or booleans.
If we had unknown implemented (see UnknownType) then this would be even cooler:
unknown `.`(row@Results, String column) {
return rs.getObject(column);
}
unknown `.`(row@Results, int column) {
return rs.getObject(column);
}
Results row = connection.execSql("select fullName from Employees");
String name = row.1; // no type casting needed
String addr = row.address;
Another advantage to overriding the `.` operator could be to implement dynamic method calls. I haven't explored this idea much, but it could be very powerful.
If allowing an override of the `.` operator proved too difficult (darn), here's another possibility. We could have a special interface, maybe called DynamicMember, or just Dynamic. The compiler could assume that any class that implemented this interface would have a Object getMember(String name) method, or something similar. It would call this instead of it's normal field lookups.
The compiler needs to be intelligent enough, however, to do normal member lookups for the normal (public, protected, and private) members of the class. It would only do dynamic lookups when members are accessed which have not been declared. This means that dynamic members may not be found at runtime. Which necessitates a possibly new Exception class for this situation, which the programmer of the dynamic class would throw.
It would be a really cool bonus, I think, if Nice could implement reflection on these dynamic members, so they would appear as if they were full-class citizens with the normal members of the class.
-- TroyHeninger - 25 Oct 2003
One question is whether this is important to use . in these cases. Sure, it looks good. On the other hand, it can hide the fact that the operation can fail. In that case, you could decide to use another operator, say `/`:
<T> T `/`(row@Results, int column) = cast(row.getObject(column));
<T> T `/`(row@Results, String column) = cast(row.getObject(column));
then you can use it with row/1 or row/"address".
The nice part about this solution is that the compiler does not need to be modified. With `.`, it should mostly be a parsing issue, but it could reveal somewhat tricky still.
Note that for SQL, one could do something even nicer using tuples:
With this, you don't even need to handle the result object. Wait, that requires that the fields are ordered, is it the case? Otherwise, you would need to pass the names of the fields you want inside the result (but then you don't need the 2).
-- DanielBonniot - 25 Oct 2003
I think it's not a good idea to overload `.` and `/` doesn't look pretty.
Why not use the [] syntax in this case?
<T> T get(row@Results, int column) = cast(row.getObject(column));
<T> T get(row@Results, String column) = cast(row.getObject(column));
Note that these casts are only needed because these java methods aren't retyped yet.
You can use these 'get' methods like:
String name = row[1];
String addr = row["address"];
-- ArjanB - 25 Oct 2003
I agree that overloading the `[]` operator is a workable solution. But I'd still like everyone to explore the concept of adding runtime members and methods. I'd like you to think again about the proposal for a class to override an interface to accomplish this, called DynamicMembers or DynamicMethods. These could be defined as follows:
The Nice compiler would compile code that accesses any undeclared methods or members without a compile error, but only when a class implements these interfaces, respectively. It would produce bytecodes that call the methods of these interfaces. Classes implementing these would be able to easily, dynamically add members or methods at runtime by implementing these interfaces.
Lastly, if no one has time to look into implementing these in the compiler, I'd be happy to take a stab at it myself. But I'd need to be added as a developer to the project. And I'd like to be given at least some pointers to where I should start looking at the necessary changes.
-- TroyHeninger - 11 Nov 2003
Could you give an example of application of these Dynamic interfaces? Above, you first introduced them as an alternative to overloading `.`, in case that is too complicated. But overloading `.` is rather simple, it's just a matter of parsing. The only question about it is wether we really use the `.` symbol (elegant, but possibly confusing) or use another symbol. The Dynamic interfaces look harder to implement, but they are also more powerful, since they are a runtime feature, while `.` is compile time. So examples would help, with comparison with, for instance, Arjan's proposal.
We will certinaly be glad to welcome you in the development team. The tradition is to first let you submit a few patches, and then grant you commit rights. That should not restrict you in the begining.
-- DanielBonniot - 12 Nov 2003
I can think of three examples off the top of my head: web programming, database programmin, XML programming, and properties. I'm sure there are many more. Let me illustrate each of these, though I don't have time at the moment to be thurough. I'll add more later...
When your page or servlet receives an HTTP request, instead of getting a simple javax.servlet.Request object, you would get a DynamicMember. All variables passed thru the URL (using GET) or thru a form (POST) would be available as runtime members of thi object. Also, the session object would be a DynamicMember, so that it's members were set at runtime throughout the life of the session. In a language I designed a few years ago, I also had properties attached to web folders/applications, and the servlet's init parameters that both used the DynamicMembers concept. I also had a temp object for each page that was dynamic, that wouldn't get passed in calls to other pages.
Each ResultSet? object should also be a DynamicMember so that each column can easily be accessed or changed as if it's part of the language. I referred to this idea in the introduction above.
XML Programming
XML would be a natural for this. Each Node would be a DynamicMember so that child nodes could be easily accessed, added, changed, or removed at runtime.
I'll add example code, if you need, when I have a little more time. But I hope I've opened your eyes to the possibilities.
-- TroyHeninger - 13 Nov 2003
For XML, you could use the DTD/schema to automatically generate a staticly typed view of the document (this is something very interesting to me, I would love somebody to explore this idea).
Could you, for instance, expand the database example, and explain how your proposal would improve over Arjan's?
-- DanielBonniot - 17 Nov 2003
The "Nice Eclipse Plugin" is a plugin for the open source Eclipse IDE. It is possible to extend the platforms functionality by new third-party plugins. This plugin enables developers to manage and compile nice project in Eclipse. Every Eclipse plugin has a unique ID, the ID of the Nice plugin is "net.sf.nice".
The funny thing is that the plugin itself is written completely in Nice.
enjoy!
opening Nice files in a Nice editor that uses Syntax highlighting
the nice.jar is integrated in the plugin lib/ folder
projectProperties implemented, invokable only in resources perspective
niceBuilder implemented that knows how to build the project
compiled binaries are stored in a jar file. If no jar file name is specified in the project properties panel, then the name of the jar file is by default "project.jar"
the main package can be set on the projects property page
invoking the compiler.
compilation progress is implemented
compilation errors and warnings are listed in the "Problems View"
design of basic icons
todo as next
Nice general Preferences
debugger
Nice compiler should use a central nice.jar on the system
If you downloaded the zipped plugin then all you have to do is to extract the zip archive and move the plugin folder in the eclipsehome/plugins folder.
If you got the CVS version, then type:
ant install
This will build the plugin sources and and install the plugin folder in the eclipsehome/plugins folder. Older versions will be removed.
create a project of type "Nice Project" with the name "nice1"
change to the "Resources Perspective"
create a new Folder for the main package like "test"
click on the "nice1" folder
Menu "Project"->"Properties"
select "Nice Project Properties"
set main package: test
leave classpath empty
set jar name: nice1.jar
create a new file "test.nice" in the "test" package
type VALID code in "test.nice"
save the file
now compilation should be started
a file nice1.jar is created in the "eclipse/workspace/nice1" folder
make the code invalid
save again
the error is reported to the console for debugging reasons
a "Problems" view should be opened with the error/errors
if the view is still empty then click on the black triangle on the right of the problems view title bar
select "Filters..."
check "Nice Problem"
doubleclick on the problem entry -> select the line in the nice code
Question:
How will this work in a multi-language project (say, Nice and Java, like in the Nice compiler source)? The source files will be opened in different editors, depending on the extension? -- DanielBonniot - 10 Jun 2003
Answer:
Yes, in the Nice-Project, that can contain diffferent file types, always the appropriate editor is opened. -- AlexGreif - 12 Jun 2003
Question:
What is Nice-Project? --Daniel
Answer:
with Nice Project I mean a special eclipse project Type, that was created by menu File > new... > Project... > Nice =. A Nice-Project knows which filetypes how to handle and how to invoke the compiler. In eclipse the terminus =Nature makes it possible that projects behave differently -- AlexGreif - 15 Jun 2003
Question:
If I write an ant build file for that project, will it be possible that compile errors are parsed correctly, depending on whether they come from javac and nicec?
Answer:
all errors should land in the task view, independently from which compiler. This is the desired solution. Isn't it? -- AlexGreif - 12 Jun 2003
Yes. But if the compiler was invoked by ant, I wonder how the information can be passed to eclipse through ant. --Daniel
If you build the project by ant, in a java-Project, all ant compilation errrors are "routed" in the Task view. If a java-project can to this, then we should be able to do this too :) -- AlexGreif - 12 Jun 2003
Question:
Does eclipse support Makefile too?
Answer:
I dont know, we have to look :) -- AlexGreif - 12 Jun 2003
I know, these are advanced features. I'm just curious. For the moment, support for Nice-only projects would already be great. Keep up the good work! -- DanielBonniot - 10 Jun 2003
This plugin is a good reason for me to try out eclipse. -- ArjanB
Looks useful to me. By the way, does the compiler offer a convenient way of obtaining the AST? Do we have a pretty-printer that will generate Nice source code from the AST? These could be important for this plugin (or others), if we want to support automated refactoring operations.
-- BrynKeller - 11 Jun 2003
No, the compiler offer no easy way to obtain the AST. And pretty-printing is only very limited because it's only used for creating *.nicei files. Both things could easily be implemented once the bossa.syntax package is completely converted to Nice. -- ArjanB
At the moment, the compilation API only allows a full compilation. It would not be very difficult to add an API to get an AST. It just needs to be defined: should we pass a package name and get back a list of AST for all files in this package and the imported packages? Or work on a file-by-file basis?
Pretty printing would need to be written (at the moment there is a toString method, but it won't create indentation, for instance). I'd suggest writing a Nice multi-method for pretty-printing, so it could be kept in a separate package. The root class for an AST is bossa.syntax.AST.
-- DanielBonniot
If you need it, I want to help the syntax highlighting since I familiar with the grammar of Nice and I have some time. -- ArjanB
We have to collect ideas how to realize syntax highlighting. My suggestion is
when the user types nice code then the parser should be called immediately to return the sequences that should be highlighted
or as an easy, static, alternative we define a set of syntactic rules like
comment - red
keywords - blue
class, interface - greenyou mean the class name? I mean the words "class" and "interface" themselves. It is just an example.
All editors that I know use the second way, they scan the source for strings and apply the rules in for of setting style and color.
Would it be possible to use the default colours used by the Java Editor (until we implement a Window / Preferences / Nice / Syntax notecard for selecting colours -- IsaacGouy - 19 Feb 2004
I think the first suggestion is better, because we let the nice parser determine which words are highlighted. On the other hand we have to measure how performant on the fly parsing is.
Maybe there is a way in eclipse to parse only the relevant parts of the edited document, and not the whole file all the time. -- AlexGreif - 12 Jun 2003
The parser is not very adapted for syntax highlighting because:
it skips comments
it does remember the location of keywords
It seems that we need three parsers, for the compiler (can be used for refactoring too), NiceDoc, and syntax highlighting. They are similar, but have different requirement. Does anybody know a way to share the common parts, while getting the different behaviours? -- DanielBonniot - 12 Jun 2003
yes that would be a good thing! -- AlexGreif - 12 Jun 2003
-- AlexGreif - 12 Jun 2003
I think implementing simple keyword highligthing is a good start. -- DanielBonniot - 12 Jun 2003
OKwe will take this for the first proto , then I can use the Ruby example :)) They do it the simple way too -- AlexGreif - 12 Jun 2003
We don't need three parsers because the NiceDoc comments could be parsed by the compiler and be put in the AST. This allow better linking between NiceDoc pages because the compiler can know wich method implementation belongs to which method definition and vice versa.
The parser for the plugin could be a lot simpler than the one for the compiler so sharing common parts might be not worth the effort.
-- ArjanB
I would prefer the parser in the final solution.
The question is, how should the parser return the syntax information?
xml?
proprietary format?
List of Classes that contain info about CharRange+Style+Color
It depends how eclipse allows us to implement it, but IMO the third one is the best.
-- AlexGreif - 12 Jun 2003
For syntax highlighting, it seems to me the best would be to implement an generic SyntaxHighlighter?. It would take as input a java.io.Writer, and return as output a list of specifications: at such position there is a keyword, at such other position there is the name of a variable being defined, ...
Would it be easy to do the highlighting in the text editor, given this info? -- DanielBonniot
That ist my third suggestion. Isnt'it? -- AlexGreif - 12 Jun 2003
It should probably be started in a low-priority thread, so it does not slow-down the system when writing. -- DanielBonniot
I have to examine how to start threads in eclipse. I think eclipse hast its own way to handle threads. -- AlexGreif - 12 Jun 2003
A good thing is that SyntaxHighlighter? could be shared among several editors (think JEdit).
If we think, in the long term, about refactoring, showing the definition of a class and automatic completion, which basically mean parsing the whole program, this could be seen as duplicating effort. On the other hand, it could be nice to have syntax highlighting on syntactically incorrect programs (when you are writing a method, and it is not finished yet), and then SyntaxHighlighter? needs to be quite different from a normal parser anyway. -- DanielBonniot
Maybe thats the way they have siple word matcher for syntax Highlighting -- AlexGreif - 12 Jun 2003
Do we know how code completion works in eclipse plugins? -- DanielBonniot
not yet :)) -- AlexGreif - 12 Jun 2003
In the first run I will embed the nice.jar in the temporary (net.sf.nice.compile) plugin. This makes it fast to implement the plugin proto. If we get more experience how to handle only one nice.jar on the whole system, we will get rid of this temp plugin. -- AlexGreif - 12 Jun 2003
Question:
Should there be a way to specify compilation options (the main package, class directory, classpath...). -- DanielBonniotAnswer:
we should specify the most necessary things in the main preferences (Menu Window > Preferences > Nice).
And project specific preferences can be set in the project itself. -- AlexGreifQuestion:
Or should we require that the user writes an Ant build file to do that? (Is there a good assistant to write build files? Or maybe the nice plugin could contain an editor to create sections in a ant build file specifically for the nicec target, listing the available options?). -- DanielBonniot - 12 Jun 2003
Answer:
Only ant script is not enough. Its not very user friendly.
There is another plugin that lets one build ant scripts. we can make our plugin dependant on the ant-plugin.
-- AlexGreif
I managed to compile a Nice project, by writing a ant build file! The editing was in a text window, but there was automatic tag completion. Is this what you were refering to, or is there something more user-friendly? -- DanielBonniot
Its another ant plugin. I deleted it two days before so I cannot tell whats it exacly doing. But it helps with editing the ant script maybe in a graphical way -- AlexGreif - 12 Jun 2003
And the compiler would generate in case of this example something like:
abstract class Coin implement Enum {
final Coin penny = new penny();
final Coin nickel = new nickel();
final Coin dime = new dime();
final Coin quarter = new quarter();
}
final class penny extends Coin{}
final class nickel extends Coin{}
final class dime extends Coin{}
final class quarter extends Coin{}
int value(Coin);
value(f@penny) = 1;
value(f@nickel) = 5;
value(f@dime) = 10;
value(f@quarter) = 25;
-- ArjanB - 30 Apr 2003
An alternative is not to declare a class for each value, but make them an instance of the same class. An advantage is that we generate less classes, which should improve runtime efficiency (loading classes takes time to the jvm). If we did that, then we cannot use class dispatch for methods. We could either compare references, or assign a unique small number to each case, and use a switch bytecode to dispatch. This is an implementation detail, and should not make any difference in the specification part. This makes it possible to use the class implementation first since it seems simpler, and to optimize for speed after the implementation works well.
-- DanielBonniot - 30 Apr 2003
We can use dispatch on value for enum by giving each element a final field(named ordinal?). It is only a bit more work.
Another implementation problem is how do we prevent the addition of elements after the enum is made. It should not be possible because it might bypass the coverage tests.
-- ArjanB
An important question is: should enums be extendible?
It's tricky because then a inverted inheritance tree for enums is needed. I think it's doable although I haven't figured out the details yet.
Because of the complexity for the users and of the implementation, Java 1.5 will not have extendible enums.
-- ArjanB - 16 May 2003
Even though I am not a fan of enumerations, I had this idea when I was walking home yesterday for a safe switch construct for enumerations. Consider, that for every enumeration, two methods switch and switch_default are automatically generated in the same package like this:
Also, assume that there is a different anonymous function syntax as Arjan hinted at in BlockCallSyntax, where "() => doSomeThing(); } is equivalent to { doSomeThing(); )". Then, we can write:
Note that this would be "safe," because if you add another enumeration value (e.g. half_dollar) then the signatures of the switch and default_switch methods change, and all callers of those methods will have to be updated in order to compile properly (like when adding a constructor to an algebraic datatype in ML). Presumably, during code generation these calls could be translated into the same JVM instructions that Java switch statements use.
Anyway, I still am not sure that enumerations are useful, and it seems like this does not have any value over the method dispatch technique below, but I wanted to present this idea before I forgot it:
int coinSize(Coin c);
coinSize(penny) = 2;
coinSize(nickel) = 3;
coinSize(dime) = 1;
coinSize(quarter) = 4;
let size = coinSize(s);
-- BrianSmith - 21 Feb 2004
I saw an implementation like the following a while ago, which I think does it quite nicely. Consider it as a pattern to be generate when ever an enum is specified:
final public class Coin {
public final static Coin PENNY = new Coin("Penny");
public final static Coin NICKEL = new Coin("Nickel");
public final static Coin DIME = new Coin("Dime");
public final static Coin QUARTER = new Coin("Quarter");
final String name;
private Coin(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
An example of usage:
Coin aPenny = Coin.PENNY; // ok
Coin aHalfDollar = new Coin("HalfDollar"); // fails due to private constructor
This has some advantages in that one can't add on to a declared enum, code is readable, equals(), hashCode(), and == operator (i.e. identity) work nicely, as does toString(). Of course, it's type-safe too. On the downside, it depends on access modifiers, which don't exist yet in Nice (but will be implemented, of course).
If one wants to associate values with the enum, then this implementation is no good, of course. Though one could probably handle that by changing from what one generates the resulting code to be like in the Java 1.5 spec and then generate the following:
final public class Coin {
public final static Coin PENNY = new Coin("Penny", 1);
public final static Coin NICKEL = new Coin("Nickel", 5);
public final static Coin DIME = new Coin("Dime", 10);
public final static Coin QUARTER = new Coin("Quarter", 25);
private final String name;
private final int value;
private Coin(String name, int value) {
this.name = "Coin." + name; // replace "Coin." with fully qualified class name perhaps. or eliminate (?)
this.value = value;
}
public String toString() {
return name;
}
public int value() {
return value;
}
}
It is often useful to declare that, in a subclass, a certain field of the superclass has a more precise type. To be safe, this feature only applies to final fields.
class A
{
final Reader reader;
}
class B extends A
{
override FileReader reader;
}
The new type must be a subtype of the original type, and the original field must be final.
Implementation notes: the type in the default constructors of B must be changed. For the field access methods, the get method can simply be overriden.
-- DanielBonniot - 21 Jul 2003
This is implemented in the DevelopmentVersion (future 0.9.1) of the compiler.
-- DanielBonniot - 02 Aug 2003
This page shows the concept of FlagInterfaces to solve the typesafety problem of the collections api.
Note: this is an immature proposal and the syntax will change problably.
A flag interface is an interface with some restrictions it can not have methods and is not an independant type. It can only be used to refine the type of a normal interface or class.
Here a selection of the declarations in the collections api enhanced with FlagInterfaces:
flag CollectionAcces Immutable;
flag Replacable extends Immutable;
flag Reducable extends Immutable;
flag Appendable extends Immutable;
flag Mutable extends Replacable, Reducable, Appendable;
interface Collection@Mutable<E> has CollectionAcces{}
interface List@Mutable<E> extends Collection<E> {}
interface Iterator@Mutable<E> has CollectionAcces {}
interface ListIterator@Mutable<E> extend Iterator<E> {}
<E> boolean contains(Collection@Immutable<E>, E);
<E> int size(Collection@Immutable<E>);
<E> boolean remove(Collection@Reducable<E>, E);
<E> boolean add(Collection@Appendable<E>, E);
<E, CollectionAcces T> Iterator@T<E> iterator(Collection@T<E>);
<E> E get(List@Immutable<E>, int);
<E> E set(List@Replacable<E>, int, E);
<E> E removeAt(List@Reducable<E>, int);
<E> void add(List@Appendable<E>, int, E);
<E, CollectionAcces T> ListIterator@T<E> listIterator(List@T<E>);
class Vector@Mutable<E> implements List<E>{}
class Array@Replacable<E> implements List<E>{}
public void main(String[] args) {
List<String> mylist = new Vector(); //convenience we take the default CollectionAccess type of List here
// List@Mutable<String> mylist = new Vector(); //so this line is the same
mylist.add("abc");
Collection@Immutable<String> coll = mylist; //coll is a immutable view of mylist
coll.remove("abc");//error not allowed
Collection@Appendable<String> c = coll;// error not allowed
}
An experiment as proof of concept of the way of typing, the commented lines are type errors:
package test;
interface I{}
interface J extends I{}
interface K extends J{}
class X<+T | K <: T>{}
class Y<+T | J <: T> extends X<T>{}
void foo(X<I>) = println("foo");
void bar(X<J>) = println("bar");
public void main(String[] args) {
X<K> x = new X();
X<J> y = x;
X<I> z = x;
Y<J> a = new Y();
Y<I> b = a;
//Y<K> c = a;
X<I> d = a;
X<J> e = a;
//X<K> f = a;
a.foo();
a.bar();
b.foo();
//b.bar();
d.foo();
//d.bar();
e.foo();
e.bar();
}
-- ArjanB - 26 Apr 2003
The problem with lazylists can be solved by an additional FlagInterfaces.
interface Sizeless {}
interface Immutable extends Sizeless{}
interface Replacable extends Immutable{}
.... // rest of the interfaces
<E> boolean isEmpty(List<E,Sizeless>); //isEmpty is possible on lazylist
<E> int size(List<E,Immutable>); // but size is not
class LazyVector<E, T+ | Sizeless <: T> implements List<E,T>{}
An advantage of using type parameters to restrict methods is that only a few interface need to added.
I think this can be used without changing the existing collections api. Only retypings will be needed.
An important part of this proposal is the default type parameter of each collection interfaces and classes.
This improves the ease of use. Only when writing new methods or using a more restrictive view of the collection class, is additional typing needed.
Daniel does the way of typing make sense?
-- ArjanB
Interesting. I would have to think more in details. It seems like it could work.
Have you seen my comments on the JINX page? yes
There I mention & types, and how they can be syntaxtic sugar for constrained types. It would be interesting to compare the two.
One issue is whether we need to be able to dispatch on "flags", or is this just to give types to existing methods. I think if we don't modify (wrap) the collection API, we cannot dispatch on, say, Immutable. Or maybe it would be the same as the list of classes that implement it? I think so
We cannot dispatch on type parameters. With & types, when you declare your own hierarchy, you could really create the interfaces, so you could dispatch.
I think with both proposals, we would have a similar API.
-- DanielBonniot
I think you should view "flags" as a kind of abstract interface so you shouldn't need to dispatch on them.
Both proposals should be worked out so we can make a good comparison.
-- ArjanB
flagtest.nice: A wrapper for the collection classes for testing
Note: this has been implemented, as of Nice 0.9.2 -- DanielBonniot - 18 Oct 2003
Imported email discussion:
Could there be a way to also be able to separate the declaration and the definition of a function ? I was thinking about this, since if we could do so, we could really divide declarations and implementations. Thus being able to write files that only contain the 'API' of a package, but not showing the implementation. -- GamsL - 19 Sep 2002
The naive way would make it look like a multi-method. So how would we distinguish them?
One possibility would be to get rid of functions, and only keep multimethods.
Simplification. There is only one concept instead of two. People don't need to learn two different syntaxes, and to understand the differences.
Better extensibility. A problem with using functions is that you might think when you declare it that you won't need to have several implementations, but then you find out later that you were wrong. If it had been a multi-method from the start, there is no problem here.
Exactly this poses a problem ! You remember I had to let the functions work with multimethods (in their body) to guarantee exhaustiveness !
Separation of declarations and implementations.
This would be very nice !
Currently calling a function is more efficient than calling a method, even if there is only one implementation. This could be overcome by an optimization in the compiler.
We would loose the ability to forbid new implementations (like final methods in Java). Often this is used to optimize dispatch speed. Since the dispatch code is written at link time in Nice, the compiler can optimize it automatically. Are there justifications for "closing" a method? It is a philosophical question. Some would say that it allows to reason about the exact behaviour of the method. Others would say that "closing" is against the idea of Object Orientation, where everything is open for specialization. Maybe the addition of design by contract can help, since it will allow to impose restrictions on the reimplementations of the method.
Will people miss functions? For instance, it seems that the Dylan language used to have only methods, and that they added functions later. So should we go the other way around? See http://www.gwydiondylan.org/old-docs/htdocs/proposal-define-function.txt and http://www.functionalobjects.com/products/doc/core/core_8.htm.
I think so too ! What if a programmer uses a final method to optimize dispatch speed ? There might be cases where this feature is solely used for this purpose, and it would be perfectly alright (or even desirable) to add a new implementation ! I guess this should not be forbidden, since DesignByContract? could maybe handle most of the cases where restrictions are necessary, anyway ?!
Remove the concept of toplevel function
Still accept the current syntax for functions. It will mean: declaration of a multi-method, and default implementation.
I like this idea, it fits well I guess !
This has the advantage of keeping backward compatibility with programs using functions. You can still declare a method without the implementation(for instance to separate it in a different section or file). We still keep the distinction between method declaration and method implementation, which I think is very important (while it is blured in Java).
Will it then still be possible to provide the default implementation somewhere else, and just define a method ? Or will it then be necessary to define the default implementation like one currently defines a function ? I shouldn't think so !
Currently you should keep on using functions when appropriate.
They are more efficient at the moment
If I do the change, the same syntax will remain valid, so you won't have anything to change in your source using functions
It is less work :-)
-- DanielBonniot - 19 Sep 2002
I was wondering if it will sometime be possible to declare inner classes in Nice ? I guess this is very useful in some cases. Inner classes are currently not accepted by the compiler.
So I think the inner classes are not even planned ? (I come to this conclusion since public and private are in the list, but also not yet implemented, or am I wrong ?)
-- GamsL - 24 Jul 2002
Inner classes are one of these feature of which I am not sure if they would add anything to Nice. In this case my policy is to wait, work on what is surely useful, and listen to arguments. They are definitely in Java, but Java has no anonymous functions (which are faked by anonymous inner classes), and is class centric, so that it is useful to put a class inside another. Are there arguments to say that Nice misses them?
Actually inner classes in Java are compiled to normal classes, which have a link to their container class (roughly, I think there are visibility issues). Which gives a hint at what to do if you really miss one.
-- DanielBonniot - 25 Jul 2002
I find I use inner classes in Java quite often, usually when writing classes that need to work together closely and to avoid polluting the public interface or resorting to package access. The way I see it, inner classes are a combination of two features. The first is the automatic parent reference. While nice, this is just syntactic sugar and can be completely simulated in other ways. The second is the access granted to the private members of the parent class. This is essentially the same as using the friend keyword from C++, and as far as I know cannot be simulated in any other way in Java (beyond reflection, perhaps).
What I'd rather see in Nice though is something like the way Eiffel does access control. Every feature of a class in Eiffel can have a list of classes who (along with their subclasses) can see the feature. For example, the equivalent of protected access could be achieved by listing the class itself. A language feature like this allows classes to co-ordinate with each other at a very fine-grained level, rather than resorting to the big hammer of package access or the dangerous friend concept.
-- Vulcannis - 21 Aug 2003
Nice allows multiple classes in a single sourcefile. VisibilityModifiers are not implemented yet but the proposal is to give sourcefile access to things with the private modifier. So it will be possible to simulate the access restrictions as in java inner classes. Details of the access modifiers aren't decided yet.
-- ArjanB - 21 Aug 2003
As I am just starting to develop a new Eclipse Plugin for Nice, I wanted to share my first ideas and goals concerning features and their realization with the community. Everyone reading this is very welcome to comment on everything that is said here! If you have ideas, feature requests, problems, you think I'm wrong, you think someone else is wrong, you know a better way to do something (maybe because you already have experience in that domain), really whatever comes into your mind concerning the nice-eclipse plugin! Don't be shy, post it here!
One thing I have to say right away is that my general approach to writing this plugin may look a bit "conservative" to some of you, as I do not really plan to rush things and add feature after feature as fast as possible. Instead I'm planning to focus on a solid design that integrates smoothly into eclipse and is flexible enough to handle advanced IDE tasks . Therefore I still need to do extensive studies of eclipse to get more insight into how things are done. I know that it would be hesitating to start implementing cool editor features right away, but I think that without a solid architecture, a project of the size this one will probably have in the end, will be a pain to deal with.
One consequence of this approach is, that it may take some time until the first "true features" will be available (as building and running are too basic to be considered true features :-) . True Features are probably those that make editing Nice source easier. Another consequence is, that once the underlying architecture is reliable, it should generally be easier and faster to add features. I hope this will work out and provide a nice piece of software. Same as with everything here, comments welcome!
The following is by no means a complete list of goals for this project. These are just the ones that came into my mind so far (minus a few I just can't think of right now)
Long term goals haven't been defined exactly yet, but it is definitely an overall goal to provide a full fledged Nice IDE that supports everything programmers coming from java are used to. The general rule should be that if some operation is possible in the (Eclipse) Java IDE, it should be possible to do it in the Nice IDE as well, along with features we come across that are unique to Nice. This is a brave goal, it will take time to achieve it!
As said on page 18 of the german edition of the book "Contributing to Eclipse: Principles, Patterns and Plugins" by Erich Gamma and Kent Beck (maybe suffering from my poor translation abilities):
"Entering the world of Eclipse, you will spend more time reading code, than writing code. You first have to get used to those incredibly productive days, where you spend 6 hours on reading and 1 hour on writing code."
You probably guessed right, I own this book :-) Anyway, the following is what I will read a lot about, and hopefully also will write enough to make it work. Sections marked as DONE are still subject to refactoring or even rewrite from scratch, as I gain more understanding of the eclipse platform. So if you think almost everything is marked as DONE, why are these still goals? Think of the current implementation as a prototype that anyway will be rewritten in Nice very soon.
This is actually the most important goal for now: get an understanding of the eclipse architecture to be able to extend it correctly and get the most out of it. It is very likely that the plugin will be developed in a few subprojects (that map to plugins) to separate logically independent parts from another. Here are two of them I can think of at the moment.
JVM Based Language Launching
As Nice compiles to java bytecode, Nice apps are run like normal Java apps. However, the eclipse code that realizes this functionality depends on the JDT's JavaModel? (a set of classes that model java source and - i think to a certain extent - bytecode as well). This leads to the restriction that plugins that want to launch programs using the JVM need always be JavaProjects? (working with a JavaModel?) in order to make use of the Java launching capabilities.
The goal of this subproject should be to provide a way for jvm-based languages to plugin their own type of project, along with their own model, and still use all the functionality and UI that Java programmers use when launching their applications. I think this makes perfect sense, as when it comes to launching, there is really no difference between java and nice (and probably most other jvm-based language). This idea came to my mind when looking at http://eclipsefp.sourceforge.net/ . Those people try to provide a common framework for functional languages (not especially targeted at launching) that should make life easier for plugin writers. Why not try to get something reusable out of a problem we anyway have to tackle?
To do advanced source code editing we need a model that represents Nice sources inside the plugin. This could be a model of source code that is constructed during a compilation, with a precise API to read and modify from the plugin, that can be sent back to the compiler for further (incremental) compilation. IMO advanced operations like refactoring, error correction proposals, searching, navigating, ... justify the additional complexity of such a model (and the changes/additions to the compiler that are needed), as they would be a pain to implement with no reliable infrastructure. This model (or API used by the model) could be implemented as a library using Nice AST. This is definitely one of the integral parts of the plugin and will need a lot of discussion, especially with core developers. Although it is no main goal for me at the moment to start implementing this, we should use the time to discuss an architecture both inside and outside the Nice compiler, that would be able to handle these issues.
-- MartinGamsjaeger - 03 Dec 2004
ConnectionHandle extends Handle
How do you know that the last element has the right type? -- DanielBonniot
The draggable points of a Connection are Handles. The first and the last are connected to flowlets, thats why they are called ConnectionHandles, and the handles in between are EllbowHandles. There can be zero or more EllbowHandles?. I defined it like this. -- AlexGreif
Interesting. Here it is the logic of the system that guarantees the cast will not fail.
I think you could take into account this logic in the types. One possibility is to keep a reference to the handles on both extremities in fields of type ConnectionHandle.
If you wish, you can encapsulate the handles in a Handle class:
class Handles
{
List<Handle> handles;
ConnectionHandle first;
CollectionHandle last;
}
// Some methods to manipulate handles.
--Main.DanielBonniot
Interesting too. But IMO not very nice design, because the class Figure (the super class of all) has the member List<Handle> handles. And I want to store all handles in one List. A Connection has this speciality that it has two types of handles. Besides this I can iterate easily if all Handles are in one List. -- AlexGreif
OK. Then could you add in Connection the two fields, while keeping everything in the list? -- Daniel
Then the info is redundant, and I have to update both. Where do I profit if I introduce two fields? -- Alex
The type information is not redundant. A benefit is that you make sure that there is no cast, that might fail at runtime in some strange condition. On the other hand, it is true that you then need to guarantee that the value in the field is the same as in the list.
If you have a set method in the parent to build the list, then you could override it to set the fields when the index is 0 or length - 1, and the call super.
I agree that the benefit is discutable. An alternative is to use the cast.
In a future version of Nice, I could imagine that you could do:
Hand h = cast(handles.get(handles.size()-1));
assert h instanceof ConnectionHandle;
// Now you can use h as a ConnectionHandle.
Flowlet extends Figure
The problem is now like with ConnectionHandle?. Could you modify the design a bit, so that the type of the expression is Flowlet and not Figure?
No. Figure is the superclass of all drawable Items. Flowlets are items that can be connected together. Connections cannot be connected. Thats why they are Figures.
Here is the class hierarchy:
Figure <- PolygonFigure
PolygonFigure <- Flowlet
PolygonFigure <- Connection
-- AlexGreif
if (connectorListeners == null) return;
for (Iterator<ConnectorListener> iter = notNull(connectorListeners).iterator(); iter.hasNext();) {
ConnectorListener ltnr = iter.next();
if (figure.getHandles().contains(cast(ltnr))) {
iter.remove();
//println(" deleted: " + ltnr);
}
}
Isn't this code equivalent to the following -- ArjanB
I think Arjan is right, I will test it -- AlexGreif
if (connectorListeners == null) return;
connectorListeners.removeAll(cast(figure.getHandles()));
You need notNull, because connectorListeners is not a local variable. This is explained in OptionTypes. A solution is there too.
What is the type of figure.getHandles() ?
java.util.List<Handle> getHandles() = handles;
ConnectorHandle, that extends Handle implements ConnectorListener
Interesting. So you have a value of type A, a List<B> with B extends A,
and you want to know if the value is in the list, right?
We were discussing with Arjan if this is ever needed.
The point is, it can only be true if the value is of type B.
But now I can believe this is really useful.
If it is really this, we will change the type of contains in nice.lang.
After that, you won't need this cast. -- DanielBonniot
I changed the retypings so the cast isn't needed anymore in the next DevelopmentVersion -- Arjan
Another example to show why contains should be contravariant.
<T,U,V | U <: T, V <: T> Set<T> intersection(Set<U>, Set<V>);
intersection<T,U,V>(s1@Set, s2@Set) {
Set<T> res = new HashSet();
if (s1.size() < s2.size()) {
for(U elem : s1)
if (s2.contains(elem)) res.add(elem);
} else {
for(V elem : s2)
if (s1.contains(elem)) res.add(elem);
}
return res;
}
Should I reapply that patch then Daniel? -- ArjanB
OK! Doesn't removeAll also need to get a contravariant type? -- DanielBonniot
Yes but if I'm consistent then should almost all methods of the collection be retyped to get a co/contra-variant type. the methods in Map will get uglier retyping then.
<K, K0, V0, V | K <: K0, V <: ?V0> ?V0 get(java.util.Map<K, V>, K0) =
native Object java.util.Map.get(Object);
Yes, it makes sense. JSR 14 (Java Generics) does use Object for the key in get. So we are still more strict and safe than them.
I don't think it matters too much if the retypings become more complicated, especially if it means they will be usable in more situations.
-- Daniel
Ok but I'm not sure about one kind of retypings:
<K, K0, V | K <: K0> java.util.Set<K0> keySet(java.util.Map<K, V>) =
native java.util.Set java.util.Map.keySet();
this is safe because a keyset is read/remove only. -- Arjan
Are you not sure it is safe, or if it is useful?
I checked the javadoc, it is true that you cannot add elements, so this must be safe. I'm not sure how useful it will be, but it does not harm to do it. Maybe it will turn out useful in some situations. And we can say that Nice allows this, while Java 1.5 cannot! :-) -- Daniel
It reminds me that I still should write something about retyping, at least the is no shortage of examples -- ArjanB
getFromConnection(state) {
java.util.List<Connection> followings = this.getFromConnections();
for (int i = 0; i < followings.size(); i++) {
DecisionConnection connection = cast(followings.get(i));
if (connection.getState() == state) {
return connection;
}
}
return null;
}
DecisionConnection extends Connection
Why don't you declare that this.getFromConnections() returns a List<DecisionConnection>? -- DanielBonniot
The code above is in DecisionFlowlet, but this.getFromConnections() is declared in Flowlet that returns Connection objects.
Is getFromConnections reimplemented for DecisionFlowlet? -- DanielBonniot
No. Only implemented in Flowlet class. Thus I have to return a List with the lowest common denom.. --Main.AlexGreif
Should I use :
-- AlexGreif
It's possible, but that's still a cast.
Note that using get on a List in a loop can be bad, in case the List is a linked list: you will traverse n**2 elements, instead of n.
This is ideal for the new for syntax of version 0.7.8:
-- DanielBonniot
OOps did I miss that? Is it documented somewhere? -- AlexGreif
I think it's in the changelog only, at the moment.
You see, Arjan, documentation is needed! :-)
-- DanielBonniot
It should be in the manual that you need to look in the changelog. ;-) -- ArjanB
:-)
Seriously, Arjan, you should write something to put in the Statements section. -- Daniel
more to come...
-- AlexGreif - 22 Apr 2003
OK, thanks. I'll treat them progressively.
For those that are solved, if you feel that some documentation should be added (for the numeric types, I updated the User manual, so that it compares with the Java syntax). After that they can be deleted.
Some will probably need to stay, when they are a good example of a typing problem, and how to solve it. So we can keep those there.
-- DanielBonniot - 22 Apr 2003
When and how should the NiceCompiler(bossa.* packages) be converted to Nice?
It has quite a few benefits but it will require a lot of time to do so.
Is the language complete enough to do this conversion? i.e. NiceConstructors?
What could be done to make the conversion easier?
Decreasing to use of Java specific features.
Adding a layer between the parser and the bossa.syntax classes.
UPDATE: As a first step, in the current development version, classes can have initializers, like in Java.
One further improvement is to allow CustomConstructors. Comments are welcome about them.
See the latest ConstructorSyntax proposal.
Nice doesn't have custom constructors but the default one is not always sufficient to express everything you want.
The implementation of field depending on previous fields will help in some case.
In what cases are default constructor not enough and what could be added to Nice to solve that problem?
One possibility is to allow that each class can have one constructor without any arguments that is called after the fields are initialized and the constructor of the superclass is called.
-- ArjanB - 09 May 2003
Given that DesignByContract? is already part of the language, it's worth noting that using factory methods for object construction can cause problems with ClassInvariants. The invariant should be true on exiting a constructor, but need not be on entering, so some way of marking the factory method as for object creation is needed.
-- SamsonDeJ - 13 May 2003
One thing that might be wrong about Java constructors is that they serve two purposes. One is to assign values to the fields of the class. The other is to do any operation that needs to be done when an object is constructed. Let's call the first aspect construction, and the second initialization (are there clearer names, or are those clear?).
The problem with mixing these two purposes arises when you start calling methods from the constructor, before the fields are set. In other words, from a Design by Contract point of view, if you call a method before the invariant is valid. This means that the method called cannot assume the invariant is true. Worse, that method can call others.
How is this handled in other languages? In particular Eiffel, since it has contracts. If, as I suppose, all (public) methods can assume the invariant upon entrance, how is the above problem avoided?
Eiffel lets you mark methods as for instance creation, and these are not checked for the invariant at the start. There's a hack to allow them to call other (non-instance-creation) methods before the invariant is properly set:
invariant
constructor_constraint: in_constructor implies [<a more limited invariant>)
general_constraint: not_in_constructor implies (<the full invariant>)
so you set in_constructor and not_in_constructor at the appropriate places. It's got great potential for misuse though. To make it work you would have to painstakingly record for each method which parts of the class invariant they really depend on, which kind of defeats the purpose of an invariant. I've got a couple of vague thoughts on the matter, under ClassInvariants -- SamsonDeJ - 15 May 2003
My design goal in this area is to solve this problem, while keeping enough flexibility.
One possibility I see to solve this problem is to separate construction and initialization. They would clearly be marked as two different phases of creating a new object. Construction is already supported in Nice with the automatic constructor, where you provide values for each field (optionally for those with a default value). It might be useful to be able to define custom constructors, that construct the values of the field in other way, for instance with some computation over their parameters. But they would not hold a reference to the constructed object (this in a Java constructor), which solved the invariant problem. (A syntax would need to be created for those).
As for initialization, it could be done via a normal method, called for instance init. Because initialization can require calling arbitrary methods, the invariant should be true upon entrance (and therefore at the end of construction). It is a possiblility to choose to provide special support for initialization. In particular, we could make sure that init is called automatically when an object is constructed, and that the parent init (super) is called.
Doesn't this cause the same sorts of invariant problems as constructors in cases where the invariant depends on initialisation, not just construction? My suggestion (summarised from ClassInvariants) is to allow methods to be marked as either requiring the class invariant or not. Those that don't require it must still maintain it if it already applies, but may be called during initialisation as well as during normal operation. -- SamsonDeJ - 16 May 2003
The invariant is normally a condition on the values of the fields. In that case, anything that makes the invariant become valid belongs to the construction, not the initialization.
Yes, generally, but there are simple cases where this isn't so. For eg., how about a doubly-linked-list Node class. We would like the invariant
but that is only true after initialisation, since the constructor can't set the next field of the previous Node.
It could help to have a list of common things that needs to be done during initialization:
Register the instance in a global list, like a Hashtable mapping keys to the instances of the class.
?
In this proposal, there could be several ways to construct the object, but the initialization would be a unique method. Comparing to Java where constructors, which serve both pruposes, can be overloaded, is this a limitation?
I'd then to think that, besides behaving differently depending on the value of the fields, initialization should be uniform. If something else needs to be done in certain cases, then that can be done as a method call at the creation site.
A summary of this could be: creation = construction + initialization.
Does this sound like a nice and practical handling of object creation?
-- DanielBonniot - 14 May 2003
I'd just like to point out that I think the default constructor as currently provided is useful and should remain available.
_I agree with this. The default constructor will remain, as it is adequate for a rather large proportion of classes. Moto: Easy things should be easy, hard things should be possible. -- DanielBonniot
There are plenty of times, however, when I don't want people to use the default constructor. Mostly it's because I don't want them to assign invalid values to certain attributes. If I do:
class A {
int i;
?int cachedCalculatedValue = null;
int getCalculatedValue() {
if (cachedCalculatedValue == null)
cachedCalculatedValue = i * 5;
return cachedCalculatedValue.notNull;
}
}
I really need a way to keep people from doing new A(i: 5, cachedCalculatedValue: -23472398);.
I've been thinking about this for a while now, and I'm now wondering about a three stage approach. The code new A(...) works like this:
1. The arguments (...) get passed to the constructor method for A. "Constructors" don't actually construct anything, however, they just manipulate arguments. I'll call them "preconstructors" to differentiate them from Nice's current constructor. You might imagine preconstructors to look something like this:
//Using class A, above
A(int i) {
//trim i to be <= 10
if (i > 10) {
i: 10;
}
}
where the assignment i: 10 changes the value of i that will be passed to the default constructor. If no assignment is made, then the value that was passed in is used. The compiler calls all the preconstructors, from most derived to least derived. This ensures that a derived class cannot change the parameters to values which the base class's preconstructor would not allow. Preconstructors on a derived class can specify values for parameters in the base class, but which they do not themselves accept:
class Shape {
int numberOfSides = 0;
}
class Triangle extends Shape {}
Triangle() {
numberOfSides: 3;
}
However, preconstructors in derived classes can assign values to those parameters too:
class SuperTriangle extends Triangle {}
SuperTriangle() {
numberOfSides: 9;
}
numberOfSides will still be 3, not 9, because Triangle() runs afterSuperTriangle?(). The double assignment should probably be reported as a warning. I'm not entirely sure about this, maybe it should be an error.
Such a warning would have to be reported at the source location where the subclass preconstructor assigns to the parameter, to be useful.
2. The system calls the default constructor we use now, with the arguments returned from the preconstructor methods.
3. The system calls the 'init' method, passing the fully constructed instance.
As an alternative to this solution, here's a simplest-thing-that-could-possibly-work suggestion: allow to mark fields as off-limits to the constructor. Here's a syntax example, using a hypothetical 'internal' keyword:
class A {
int i;
internal ?int cachedCalculatedValue = null;
int getCalculatedValue() {
if (cachedCalculatedValue == null)
cachedCalculatedValue = i * 5;
return cachedCalculatedValue.notNull;
}
}
and now doing new A(i: 5, cachedCalculatedValue: -23472398); would result in an error message like "A.cachedCalculatedValue is an internal field, and cannot be specified in the constructor".
Note that 'internal' is not the same as 'private' - internal fields may or may not be private, they just can't be specified in the constructor.
-- BrynKeller - 14 May 2003
This concern about the default constructor exposing too much is valid. It must be adressed.
Your first proposition is worth considering. However, my first take on it is that it looks quite complex.
The second is much clearer at first sight. Couldn't we go one step farther, by using 'private' for this purpose? I expect them to coincide extremely often. (In your example, you would probably want 'private internal'.) In cases where they don't, you can still provide a getter (and possibly a setter), or even use the property-like feature when it is added. The planned semantics for 'private' is that the visibility is limited to the file. So it comes naturally that you cannot refer to a private field in a constructor call, outside of the file declaring the class. Inside that file, you should be able to refer to it, for instance if you declare a custom constructor.
-- DanielBonniot - 15 May 2003
The default constructor which takes "any" variables is an interesting academic idea, but it's really ignoring the benefits of constructors.
Constructors provide an interface contract which explains which paramaters must be provided for a class to work.
Constructors allow you to completely hide the implementation details of your class (i.e. by having zero public instance variables), but letting the user supply initial values via the constructor only.
With the Nice language's concept of the "sloppy default constructor", users will have to read comments to know what to provide (and those comments will get out of date) and we will have to have complicated DesignByContract? mechanisms in order to then allow class writers to enforce proper class construction.
Just add regular constructors. If you think that most constructors are simply instantiating all instance variables, please go take a survey of real-world code.
- DavidJeske
Hi David,
I've added comments in italics - DavidJeske
Thanks for your contribution. We are well aware that having only default constructors is not the perfect solution yet. In particular, we know that instantiating all fields is not the only way to use constructors. By allowing users to construct objects with "whatever fields they want" you are placing an unnecessary burden on the object-author to handle unknown initialization cases, and on the object user to figure out what the useful initialization cases are
First, let me explain why "regular constructors" are not perfect either. The most obvious problem is that they are inherently unsafe. They allow you to refer to this inside the constructor, even before the object is completely constructed. This is alot saver than not knowing whether or not the user will suppily enough fields to construct the objects, because you have no control over the constructor AT ALL.
Here is a simple example, in Java:
class Parent {
Parent() {
// We want to log the construction of Parent objects
Logger.log("Instance created: " + this);
}
public String toString() {
return "Parent";
}
}
Is this class correct? One would easily think so. And indeed, it will work properly by itself.
Now imagine a subclass is created:
class Child extends Parent {
/** @file a non-null File */
Child(File file) {
this.file = file;
}
private File file;
public String toString() {
return "Child, with file: " + file.getCanonicalPath());
}
}
Apparently, we did nothing dangerous. First thing done in the constructor is to set our private field. We require that the given file is non-null. So toString should never fail with a null pointer exception, right?
Well, it will. This is because the parent constructor is called before the child constructor. And the parent calls toString, when the file field is not set yet. Boum!
I fail to see how Nice's "no constructor" system fixes any of this. In Nice, the user might supply zero paramaters to the constructor, and then what do you have?
So this inocent looking, two class example fails. I think many developers would not spot the bug if they were just given the source. Probably, after the bug is revealed, they will find it in not much time. But what about more complex examples, involving large hierarchies of classes?
This is why we want to think about alternatives to "normal constructors". We are convinced that a better design is possible. Okay, I'm all for that, but the current step Nice has taken is a step back IMO. I'll have to think about class factories, and other things and get back to you with some better suggestions
Now you have a very good point, which is that even if we try to improve some aspects, we should not go backwards in others. Here is a tentative list of desired features:
it should be possible to define the construction interface of a class. This will be done with custom constructors.
it should be possible to hide the implementation of the class in terms of fields.
I think this is achieved by two means. First, it will be possible not to export the default constructor. I don't understand why I would ever want to export the default constructor Second, if a class used to export the default constructor, and you want to change the implementation without changing the interface, you can hide the default constructor, and define a custom constructor with the same interface, that does the appropriate construction internally.
One last point that should be mentioned: it is planned to allow fields to be declared as public-read, which means they can be read publicly, but not writen to (read access being either package, or private). I like this feature Furthermore, it is possible to replace such public-read fields with a methods, without changing the interface of the class. This is fantastic! This feature lifts two of the main reasons that public fields in Java are considered bad, and should be avoided. Even though public fields should not be used, I don't see any reason to discourage the use of public-read fields. I even like the idea of public write fields if you can do the same trick where they can transparently be values or methods. This is like C# properties without the interface-breaking change from a field to a property. This is how it should have always been done. We have all this fancy runtime JIT stuff, we should be USING it for something.
A few answers to the comments. First let me restate that we are discussing future design. The default constructor is not in itself the solution to these problems. In the final design, the class designer will have complete control over the construction interface.
We'll be glad to look at your suggestions about class factories. Also, could you provide an example of the problem with C# change from field to property?
It's great to have input from different people. This really helps the reflexion, and should lead to a better design in the end :-)
-- DanielBonniot - 19 Jun 2003
From: DavidJeske - 19 June 2003
Here are some thoughts:
public fields
Code readability is easier if accessing public fields uses assignment syntax (a.foo = 1 instead of a.setFoo(1)) C# addresses this by adding "properties". This is a special syntax for get/set methods which yeilds class members which look like fields and act like methods. Here is an example:
class Foo {
public int count {
get { return count; }
set { count = value; }
}
}
However, this creates confusion for developers:
you have to decide up front whether you are going to use a field or a property, because they are not type compatible for consumers of your interface what is the incompatibility for the consumers?
both fields and properties "look" the same but act differently
the above 'pass-thru' implemention is required if you want to provide code in the future, cluttering class definitions
The C# implementation does not allow different visibility for the get and set portions of the field
Ideally, the developer could start with a field, and later migrate to having a method implementation behind the field without changing the classes interface. Since JVMs and CIL both use really-fancy JIT compiling, the "null case" for the method implemention should be easily inlined out when there is no implementation.
class factories and flexibility
Class factories are a useful feature where downstream code can control how other code allocates objects. Just like an application re-uses the code of a library from above, by replacing the code beneath a library you can augment or repurpose that library.
Very useful tools make use of this functionality, but they normally have to go through gyrations to do it. For example, malloc-debug can sit underneath your code and tell you useful things about memory allocation. A debug version of glibc can have instrumentation code which isn't in the normal version. You can see this pattern in effect inside many places in Java. One such place is the socket libraries. Even though you don't have control over the creation of sockets when using the JavaMail IMAP support, you can get it to use SSL for the sockets instead of normal sockets. You do this through an ad-hoc mechanism involving the string names of the classes you would like the factory to use. Here is an example:
There are several problems with the JavaMail mechanism which are common to other implementations:
the IMAP designer had to build in this class factory mechanism explicitly. If the designer didn't think to put in factories, "you're screwed".
the names above are not strongly resolved, they are just strings
basic types such as "String" don't have factories
Ideally any object allocation would be controllable by providing built-in configurable object factories at the language level. One possible implementation would be to require that all allocated objects be "class fields" at the class or module level. By providing alternate classes to the class fields, the object would allocate and use a different implementation.
Someone out there is ready to jump up and down and say that "parametric types are the solution"! By providing a parametric SocketType? paramater, a library can easily allow me to override the socket type which is created by my class. Parametric types only solves one of the problems I listed above. The first and most important problems still exists, namely that if the original class designer did not think to make it parametric, then it's not possible for me to do so later.
Perhaps there is a deeper relationship between class factories and parametric types which can be created which will be sure that this paramaterization is always available. For example, perhaps we can implicitly make classes which are allocated in a class part of the paramaterization of that class. Here is a not too well-thought out example:
This code fragment:
class IMAP {
Socket conn;
IMAP(String server) {
conn = new Socket(server);
}
}
Would be automatically converted into:
class IMAP<S1=Socket implements>
Socket conn;
IMAP(String server) {
conn = new S1(server);
}
}
One thing to consider is that, since there are many more Java programmers than Nice programmers, it should be possible to write libraries in Nice whose intended audience is Java programmers. Ideally, the compiler output would be a jar file and javadoc that look as if they were written in Java. So it would be useful to be able to write constructors that appear in Java to be ordinary Java constructors.
-- BrianSlesinsky - 21 Nov 2003
That's a good point. Default constructors are already compiled as ordinary Java constructors, and can be used as such. I believe it is also possible to compile CustomConstructors as ordinary Java constructors. One difficulty comes from overloading based on argument names: if you have both new Point(double x,double y) and new Point(double angle, double distance), then both cannot be compiled to a Java new Point(double,double) constructor: that would be ambiguous since Java cannot make a distinction based on argument names.
-- DanielBonniot
Getting rid of boilerplate constructor code is fine; although, as noted above, we may just have moved the workload from writing the constructor to updating parameter names at the call site (after changing class variable names).
Not really, because with CustomConstructors you can keep the constructor API even if you decide to change the field names. And if the old names do not make sense at all anymore, then likely it means that you are changing their semantics, so the clients would better be aware of the change (like changing x,y into angle,distance).
Could we avoid that issue by providing parameters in the class definition? This Scala example might fit with Nice's labelled parameters.
-- IsaacGouy - 27 Jan 2004
Isn't the issue solved by CustomConstructors? In Scala, it the constructor arguments are not labelled, which means you cannot precise the meaning of the values you pass.
-- DanielBonniot - 27 Jan 2004
Linking field names, to the constructor (and then being forced to use those field names in the constructor call) feels wrong to me. Seems better to have labelled constructor arguments - and have the programmer take responsibility for setting the fields to the argument values. (Like Scala but require the labels to be used in the constructor call.)
Are there Nice features that rely on there being a custom constructor?
-- IsaacGouy - 27 Jan 2004
We don't absolutely link field names to constructors, we just make that the default, and give you the tools to change it easily if you want or need.
If you want to have another behaviour, it will be possible: mark the default constructor as private, and write a custom constructor which sets the fields. My idea is that in many cases the default constructor will be sufficient and natural, so you don't need to put effort into doing it yourself, obfuscating (ever so slightly) the source. Doesn't having scores of x = this.x; in Java or var x: Int = xc; in Scala feel like a hack?
Something you cannot do with the Scala approach is to have a Point class with both interfaces new Point(x:..., y: ...); and new Point(angle: ..., distance: ...);. You could have either (and they would not need to match the internal representation), but you cannot have both. This means you cannot hide some representation choices to the clients even if you decide too, and you cannot provide an backward compatible way to upgrade the construction API of your class. So you are back in the case where you should always write factory methods, just in case...
-- DanielBonniot - 28 Jan 2004
We don't absolutely link field names to constructors, we just make that the default
And it's a default I will in every case over-ride!
I don't like writing x = this.x; but I really hate having field names public. Hey I have something in common with Bertrand Meyer ;-)
This is just about the only thing in Nice that I actively dislike.
-- IsaacGouy - 29 Jan 2004
Isaac, are you sure that the reason that you hate them is not because, in Java/whatever, they expose an implementation detail that cannot be changed afterwards? Can you give a reason why they would be bad in Nice with this proposal?
I would be very glad to know you dislike nothing in Nice :-)
-- DanielBonniot - 30 Jan 2004
Can't this be solved by VisibilityModifiers? You could say that the implicit constructors are package-private by default. Then you don't have to worry about the fields of a class being exposed in the public interface. Or, if implicit public constructors must be supported then their interface is defined by the public fields of the class. If you have non-public fields then you must write a CustomConstructor?. Public fields would presumably still be exposed as (implicit) getter/setter methods outside of the package defining the class. -- BrianSmith - 29 Jan 2004
Sorry, Brian, I'm getting confused. Are you answering to Isaac or to me? What is "this" that would be solved? -- DanielBonniot - 30 Jan 2004
"this" = "the problem of exposing implementation details (non-public fields) via constructor parameters." If non-public fields are never exposed in constructor signatures then it seem to support Isaac's request while still "getting rid of boilerplate constructor code" in many (most?) cases. -- BrianSmith - 30 Jan 2004
The visceral reaction is because they expose implementation detail period. Done so much of that pure object Smalltalk stuff that exposing implementation detail is a last-resort, special-case, optimization kind-of-thing. Definitely not a default case, every constructor kind-of-thing.
Or maybe it just seems ugly to have _aa as a constructor label ;-)
class A {
private int _aa;
int aa() = _aa;
int aa(int x) = _aa = x;
}
void main(String[] args){
let a1 = new A(_aa: 2);
println( a1.aa );
}
And that was actively dislike, there may be other things ;-)
-- IsaacGouy - 02 Feb 2004
Isaac, I am having trouble understanding your last comment. Wouldn't your objection be solved by making the implicit constructor for every class package-private by default? Then, code outside the package cannot access the constructor at all, regardless of the visibility of the fields. In your example, it seems that main would be able to see the _aa field anyway because it is defined in the same package, so you don't need the two methods named aa. If you moved main to another package then you wouldn't be able to instantiate class A at all without writing a custom public constructor or a public factory method. So, it seems like my idea supports your request? -- BrianSmith - 02 Feb 2004
Isaac, we all agree that implementation details should not be exposed. Again, in Nice with the current proposals (CustomConstructors and PropertySyntax), no implementation details will be exposed to clients, so there is no reason not to use public/package fields.
For instance, for you example you can simply write:
class A {
int aa;
}
void main(String[] args){
let a1 = new A(aa: 2);
println( a1.aa );
}
If later you want to change the implementation of A so that aa is not a field, you will be able to do it, without changing the API of the class.
-- DanielBonniot - 03 Feb 2004
It would be nice to have a tool to convert a set of Java classes to Nice syntax (e.g. stripping out the typecasts, putting argument names in all method calls, etc.). Even if it only did of the job, it would still be useful as a head start on moving an existing project from Java to Nice. For reference, see the tool that Apple provided in WebObjects? 5.1 for converting projects from Objective-C to Java:
http://developer.apple.com/documentation/LegacyTechnologies/WebObjects/WebObjects_5.1/JavaConverter.pdf
-- TWikiGuest - 23 Jun 2005
We need a javadoc-like tool for Nice. If possible, it would be good to re-use someone else's existing backends and just write a frontend for Nice.
-- BrynKeller - 26 Jul 2002
A more general problem is parsing. Good parsing tools will be needed for NiceDoc and also for the EclipsePlugin. I have to say in general I like ParserCombinators better than lex/yacc - style parser generation. Anyone have such a library already?
-- BrynKeller - 25 Apr 2003
Not that I know about. Writing one in Nice could be a good test/exercise. Other options for parsing in NiceDoc: write a parser using an open-source parser generator like ANTLR, or reuse the existing JavaCC? parser used in nicec.
-- DanielBonniot - 06 Jun 2003
Following on the idea of reusing parts of the compiler, I have started improving the compilation API to make that possible. The current version (0.9.2) of the compiler offers an API to load the source of package, without generating code, but instead returning a list of packages, each one containing a list of the toplevel definitions in the package. I put together a demo for this feature, which could be the embryo of NiceDoc. You can get the source here.
-- DanielBonniot - 13 Sep 2003
Chetan Mittal
Nice work! It might be good to keep some visual identity, though. Is it possible to do a small version of the first logo, that would be suitable for eclipse? It would just have the 'Ni' part (if I read the logo correctly :-).
(You should register as a Wiki user. Then you don't have to put your email on each page. And that will also allow you to get email notification when the wiki changes).
-- DanielBonniot - 01 Jun 2003
Object is currently a sort of alien in Nice. It is not a super type of all types, nor of all class types like in Java. This is the case because in most situation your are better off using type variables. When using a Java library that uses Object in its API, it's usually best to use a retyping to give it a proper strict type.
It would also make the type system weaker to have a global super-type, because for a method declaration like:
<T> boolean `==`(T,T);
we want "ABC" == myFile to be a type error.
If Object was a proper type, it would not be the case.
So currently, Object is an accepted type in Nice, but it is not related to any other type. One can convert a value to have type Object by using the method object(...).
There are still reason why we might want to add more support for Object. To avoid the problem noted above for methods like ==, Object would not be a proper type, in that type variables cannot be instantiated to Object. On the other hand, it would be consider as a top type, which means that any type is a subtype of Object.
Incorrect type in assignment to o
Found : java.lang.String
Expected: java.lang.Object
Even when superior solutions exist that do not involve Object, some newcomers to Nice will sometimes try to use Object. In this case, it would be better to have a less surprising behaviour.
Note that with Nice's dynamic type inference, even when using Object you can avoid casts:
Object f = operationReturningAnything();
if (f instanceof String) {
// f has type String here, no cast required.
int len = f.length(); // String operation, resolved at compile time.
...
}
So even without using more advanced features, users can get typing benefits when moving from Java to Nice.
For Java libraries that use Object, the best thing to do will most often be to provide retypings. Still, this proposal will ensure that even without retyping, the typing will be sensible and unsurprising, like in Java.
Moreover, there are some libraries wich use Object to represent some heteregeneous set of classes (for instance, a value that can be eith a String or a File). While this design is very questionable, these cases still occur. The proposal would make it more natural to handle such cases.
If for some reason, you need to put unrelated types in an array or a collection (for instance to pass it to a Java library), you could declare it as Object[] or List<Object>, and put any value in there.
Currently, reflexion operations are typed polymorphically. This means that you don't need explicit casts when using reflexion. However, if you want to read an arbitrary value by reflexion, the only safe type you can give for a local variable that holds that value is Object. If you want to also assign a known value to that variable (for instance a default value), then you need this proposal.
This is a generalization of the previous case. If for some reason you need to use an unknown type, Object needs to be used. It is only better to have the information that any value can be assigned to that variable.
-- DanielBonniot - 17 Nov 2003
A repository specifier can be placed somewhere on the classpath (typically at the end). This means that if there is import some.pkg; in the source, and that package does not exist locally, it we be looked up in the repository. If it is found, the jar file for the latest version is downloaded, and stored in a cache. Then everything happens as if that jar was on the classpath.
There will be an official repository at http://packages.sf.net/repository/, and it should probably be included at the end of nicec's classpath by default.
The cache can be shared between projects, since jar filenames include a version number. The cache must be created somewhere the user has write access to, so on unix it would be in ~/.nice-repository/ (~ being user.home). What about other platforms (windows)? Is user.home always a good place?
Once a project starts to use a version of an imported package, that version should stay the same until explicitely requested. Therefore, the version number must be stored somewhere. At the moment it's in a file nice.versions in the current directory. It contains lines of the form some.pkg:version. This file should be commited to the source code repository (e.g. CVS) for the project, so that every developer uses the same version, and that upgrades are tracked.
Explicit upgrades should be possible, for instance by edition of the nice.versions file. In addition, there could be a tool that tries to automatically upgrade imported packages to their newest version. After getting the new versions, it would try a compilation then unit testing. If all goes well, then the nice.versions would be automatically updated.
This is about creating packages and uploading them into a repository. First, a version number must be chosen. There is a manually chosen prefix, and an automatically attributed suffix. The prefix could be specified in the nice.versions file in the entry for the package itself, and default to 0.0 if absent. The suffix would typically be a representation of the UTC date and time, so it grows over time. This allows several versions of a package to continue to evolve in parallel. Changes of the prefix could be done when an API or behaviour change needs to break backward compatibility, and automatic upgrades would not cross those version changes.
Once the jar is created, it needs to be uploaded to a repository. Version a.b.c for package foo.bar would be located at foo/bar/foo.bar-a.b.c.jar inside the repository. It can be =scp='ed manually there. A small publication program can automate this. The question is whether it's worth to include a Java implementation of ssh (jsch is 84K in jar format), or rely on ssh/scp being installed on the system.
Alternatively, it's probably a good idea to promote continuous builds of published packages. It's especially important when packages start to import each other: you need to make sure they can use the latest versions, otherwise it will become difficult to find compatible versions. We have CVS space on package.sf.net, so we could encourage people to put libraries there. This would open the possibility to have discussions for package names, to make sure there is some coherence. Then we can install a single continuous build script, which would be triggered by CVS commits, and rebuild everything that needs to, then publish the resulting packages if they work. This is where the date suffix in versions becomes particularly handy.
Comments are welcome.
-- DanielBonniot - 02 Aug 2004
Definitely need a java version of ssh, as ssh/scp are most definitely not available on Windows machines, and it can be a bit of a chore to find one that works the way you want without a lot of configuration difficulties.
I'll write some more later when I've had a chance to review in more detail.
-- BrynKeller - 04 Aug 2004
About ssh, the question is whether if we expect people to upload new versions manually. As I said in the part about pulication, it might be a better idea to rely on continuous builds, in which case it's not a big deal to expect ssh to be present, at least for the time being.
-- DanielBonniot - 23 Aug 2004
Ah, okay. I'd vote in favor of continuous builds, then.
-- BrynKeller - 23 Aug 2004
Parser combinators are a functional approach to parsing that uses a combination of functions to define the grammar. Some (like me) find that this leads to a pleasantly natural expression of the parser. Also, it means you can write your parser entirely in the host language (Nice, e.g.) rather than having to learn a new, specialized language for your parser generator.
Haskell examples:
Imported from SourceForge? RFE #672069
Background:
Many functional languages provide "curried" functions,
of the form a -> b -> c, rather than the more typical
(in Nice) (a,b) -> c. I think providing curried
functions by default causes much confusion (and obscure
error messages), so I think Nice's approach is good.
But curried functions are often handy too.
Feature:
It would be good to have a compact syntax for partially
applying a function. I'll suggest '_' and '...' as the
symbols to use, where
'_' means "omit this argument" and '...' means "omit
all remaining arguments". So, for example:
-- BrynKeller - 13 Feb 2003
I like this idea of using '...' for curried function. There is a good chance it will get in before 1.0 . But I'm not sure about using '_'. An alternative for '_' is using named parameters as in this example:
I think I prefer the naming syntax over the positional syntax.
-- BrynKeller - 05 May 2003
I like this idea of using '...' for curried function
Couldn't the naming syntax apply to this also?
From calls to agents presents a very general mechanism that has been introduced into Eiffel.
Here's another syntax for Currying, multiple parameter lists modN(n: Int)(x: Int) = ((x % n) = 0);
-- IsaacGouy - 20 Jan 2004
I think this last syntax is not special, it's just what you get when you define a function with multiple arguments as a function taking one argument and returning a function. That is, you could already do this in Nice:
int->boolean modN(int n) = int x => (x % n) == 0;
OK, we could accept the syntax modN(int n)(int x), but I'm not sure it brings much.
-- DanielBonniot - 21 Jan 2004
Isn't this syntax just implicit curry, a la ML or Haskell? This is exactly what I don't want to see in Nice, I think explicit currying is better. I think it needs to be easy, but not implicit.
-- BrynKeller - 22 Jan 2004
Yes, it's a form of implicit curry. Note that we could perfectly have both:
int modN(int n, int x) = (x % n) == 0;
int modN(int n)(int x) = (x % n) == 0;
The second line defining a curried function. I don't think that would hurt, since uncurried versions would likely stay the "default". This would just be an easier syntax to define named curried functions. I just think that the PartialApplicationSyntax is more prioritary, but I don't think this syntax for curried functions is incompatible or would hurt in any way.
-- DanielBonniot - 23 Jan 2004
While we're discussing syntax, is there a rationale for using -> in the type but => in the definition? Would it be reasonable to use one of those in both places?
int->boolean modN(int n) = int x => (x % n) == 0;
-- IsaacGouy - 01 Feb 2004
Yes, that's a valid remark. I think in that case we should settle on -> for functions, which would allow to use => for logical implication. Is there a consensus on this change? -- DanielBonniot - 02 Feb 2004
I think I objected to this suggestion a while ago, because things could get confusing with anonymous functions:
[ `+`, `*`, `-`, `/`].map( (int,int) -> int func -> func(4,5));
but on the whole I'm not really convinced of this danger anymore, and parentheses can always be used if clarification is necessary. I'd go along with getting rid of => for functions.
-- BrynKeller - 03 Feb 2004
Mildly amusing that Scala chose to use => for both and we seem to be chosing -> for both.
-- IsaacGouy - 24 Feb 2004
We already have a comprehensive TestSuite, to check the correctness of the compiler. It would also be very useful to be able to automatically mesure performance. This falls into two categories: performance of the generated code, and performance of the compiler itself (compilation time). These tests would help make sure that new versions of the compiler do not degrade performance, and when we work on optimizations that they indeed improve performance.
-- DanielBonniot - 07 Nov 2003
I was thinking if it maybe would be a good idea to let the compiler help when writing properties.
What if we could let the compiler automatically create get/set functions in the same fashion it does with the automatic constructor ?
Of course it is not sufficient to generate get AND set all the time, but we could use keywords like
get (read) (r)
set (write) (w)
getset (readwrite) (rw)
We could then declare fields like:
class Test
{
private read String someString;
private readwrite int someInt;
}
and then be able to call getSomeString() but NOT setSomeString(s) (the compiler knows that set is not defined since someString is only read'able)
Another idea (I like even better) would be to then be able to write
Test t = new Test
(someString: "Test"
someInt: 3
);
println(t.someString)
//but NOT
//t.someString = "AnotherTest";
A problem that I see with automatic generation of this methods (operators) is that there should be a way to override them ! This makes me think of the way C# handles properties (very nice I think)
in C# you can write:
class PluggableContainer
{
private SystemState state;
public SystemState State
{
get
{
// found no actives and no inactives at the beginning
bool foundActive = false;
bool foundInactive = false;
// loop through all IPluggable objects
foreach(IPluggable pluggable in items)
{
if(pluggable.Active) foundActive = true;
else foundInactive = true;
}
// try all possible combinations
if(foundActive && foundInactive) this.state = SystemState.PartlyActive;
if(foundActive && !foundInactive) this.state = SystemState.Active;
if(!foundActive && foundInactive) this.state = SystemState.Inactive;
if(!foundActive && !foundInactive) this.state = Systemstate.Error;
return state;
}
set
{
if(value == SystemState.Active) SetState(true);
if(value == SystemState.Inactive) SetState(false);
}
}
}
value is implicitely generated and contains the value to set (always the same type as the field)
So what about a combination of these ideas ? we could generate the methods with default behaviour, if more special instructions are needed we could override them.
The compiler could then (somehow:-) know that when he sees a Person.getName method he can also use String s = p.name but not p.name = "gamsl" since he knows no setName method ...
Knowing nothing about compilers and language design in general .... what do you guys think :-)???
-- GamsL - 20 Jul 2002
In my experience, I have found that protected fields have the same problems as public fields in classes. That is, once you set a field as protected, you are no longer able to control access to the fields by subtypes. What I would like to see in the language would be that all fields default as private. If the user wants to make a field protected or public he would override the method with their implementation. An example:
class PropertySyntax{
private String property1;
}
would be transformed by the compiler to:
class PropertySyntax{
private String priv_property1;//this variable would not be reachable by the user of the language. If the user wants to set or get the value, they need to use the methods provided
private String property1();//the get method
private void property1(String property1);// the set method
}
//default get implementation
property1(syntax){
return syntax.property1;
}
//default set implementation
property1(syntax,property1){
syntax.property1=property1;
}
Now if the user provides their own methods to the class then those methods should be used instead of the default ones. So lets say I want to make the get method public and change the implementation of the
set method but keep it private.
class PropertySyntax{
private String property1;
public property1();
}
//since I have not specifed an implementation of property1(); the compiler will auto-generate the method but with public access instead.
property1(syntax,property1){//no need to declare this in the class since the signature is assumed by default
super;//call the super classes version. Or in the case of private or base class use the default version
//do stuff
}
now when the user of the class wants to get the field they should be able to do it 2 ways:
PropertySyntax syntax=new PropertySyntax(value);
//method syntax
syntax.property1();
//or the can use field syntax
syntax.propert1;
//althought the example above does not allow setting. if it did you would have the following
//method syntax
syntax.property1(value);
//field syntax
syntax.property1=value;
The advantage to the above approach is the following:
no need to change the syntax of the language.
enforces good OO practices by keeping fields private.
Field syntax makes it as easy to set fields as it would be if the field were public.
Thoughts and questions welcome.
ArthurSmyles 23 Mar 2003
I think a decision about this should be taken soon since details of others things like constructors depend on this.
-- ArjanB - 03 Jun 2003
Could you describe how they depend on this? What would be the consequences?
-- DanielBonniot - 04 Jun 2003
I don't know what's been decided on this, if anything. But I'm generally in favor of the ideas propsoed above, especially those by ArthurSmyles. I like the C# solution, though I'm not stuck on the syntax. The goal is that all code outside of a class should not know or care if a member is implemented directly accessible, or hidden behind a method or methods. That allows for the greatest degree of refactoring by the class implementor in the future, by lowering the degree coupling, which is always a good thing in large projects.
-- TroyHeninger - 12 Nov 2003
What is holding this back is mostly the lack of manpower. We will get to implementing it eventually, but the more people are willing to contribute, the faster things will move. Would you be interested in helping (with this or another aspect)?
-- DanielBonniot
We need a documentation for the standard library. The best way would be to write a tool NiceDoc, similar to javadoc. There are already documentation comments in the library.
Writing nicedoc is a project in search of a volunteer to start it!
Another thing I've been struggling with, is that the current collection hierarchy is somewhat imprecise - Collection specifies a number of operations which commonly appear together, but which might not always be appropriate (e.g., it's debatable whether LazyVector? should have a size method), and the same is true of sequence. I'm toying with a much more granular hierarchy at the moment, which I'll detail later. It also has two hierarchies, one for collections which allow in-place modification, and one for collections which do 'functional update', that is, return copies of themselves with the changed values. More later. -- BrynKeller - 05 Aug 2002
I also have concerns with this UnsupportedOperationException. It seems that the interface hierarchy was not well designed. Anybody interested to propose a fixed hierarchy?
-- DanielBonniot - 09 Sep 2002
Here a page discussing this problem: http://jinx.swiki.net/87
see also FlagInterfaces for an experimental approach to solve this problem -- ArjanB
see also StandardLibraryMethods
Are the methods in the StdLib? a complete set of additions to the collections api or do we need more?
A few names of the methods could be could changed to fit better the java parts of the api.
has->contains, keep->retain and "find" shouldn't throw an exception.
-- ArjanB
I added contains and retain in CVS, and made has and keep deprecated. We will remove them in the future, preferably waiting some time after the compiler starts emiting warning when using deprecated methods. I also renamed findIndex into indexOf, do we agree on that too?
About find (and findLast), there are two versions. One works only on collections of non-null values, and it returns null if no element was found. The other one works on any collection, but throws java.util.NoSuchElementException is no element was found. So you can choose your style: either checked the returned value, or handle an exception. The advantage is that there is never confusions between a null element in the list passing the test and no element being found.
What is the problem with this approach?
It might be practical to give the two versions different names, so one can really choose which one to use in every case. I thought about find and search. The idea is that find is expected to always return a value. If it doesn't, it is exceptional, so it throws and exception. On the other hand, search is merely searching for something, which might not be there (in which case it returns null).
Comments?
-- DanielBonniot - 10 Jun 2003
Are the methods in the StdLib? a complete set of additions to the collections api or do we need more?
I think there's room for a lot more collections methods in the standard library - in fact I'm working on some now, hopefully I'll be ready to circulate them for comments some time in the next two weeks.
-- BrynKeller - 11 Jun 2003
Can you give a preview of what you have? If you are short in time, I could help implementing the additions.
Have you considered the addition/overloading of operators for common operations on collections? I think of an operator for creating ranges.
-- ArjanB - 29 Jun 2003
About "throws Exception" or "return null", I choose both in a similar problem without change the name of the method. My suggestion is to provide 2 methods find(<K> key) (throws NoSuchElementException? if not found) and find(<K> key, <V> defaultValue) (return defaultValue if not found), like this no method name confusion (and always need to remember wich one throw the Exception), and you could store/find 'null' value and use an other defaultValue. An other variant is to define a property of the Collection to tell if you want to throws Exception or return a defaultValue.
-- DavidBernard? - 02 Jul 2005
I've noticed that there's a need for another basic method on Collection, which is a method that's like foreach, but provides the client code with some way to stop the iteration. I'll call this method forbreak, because it's foreach with a break. It looks something like this:
So the function passed to forbreak takes an 'escape continuation', so-to-speak, and returns a function to iterate with. Then the iterator function can use the escape continuation to break off the iteration when desired. Foreach can be implemented trivially on top of forbreak:
and it seems like a more generally useful building block for functions on collections which may or may not be sequences, or support size(), or what have you. For instance, find() is currently defined on Sequence, but with forbreak installed, it could be promoted up to Collection. If we added a method mkEmpty, which would produce an empty collection (i.e., much like a no-args constructor) to Container, then we could define foldLeft, map, filter, etc. top of forbreak. So for collections to get all that great functionality, they only have to define mkEmpty and forbreak. Actually, mkEmpty plus either forbreak or fold would do. Maybe we should make fold the base operation, and forbreak can easily be built on that as well.
-- BrynKeller - 05 Aug 2002
And alternate design uses exceptions. You make the iterating function catch an exception called Break. In the anonymous function, it is possible to break by just throwing that exception.
One advantage is that we can add that functionality to foreach itself, and clients don't need to do anything if they don't need breaking.
foreach(c@java.util.Collection, f)
{
try {
for (let i = c.iterator(); i.hasNext();)
f(i.next());
}
catch(Break ex) {}
}
Usage:
List<String> l = [ "A", "B", "C" ];
l.foreach(String s => { println(s); if (s.equals("B")) throw new Break(); });
Does someone know (or can make experiments to find out) if the try block would cause a performance penalty, in case the client does not need to break?
Would foreach with Break exception support a good solution? Is forbreak still needed, because it is more general?
-- DanielBonniot - 06 Jun 2003
I think it would be a perfectly acceptable solution. The try/catch overhead probably isn't worth worrying about, since it's only getting set up once. It also has the advantage that if we implement map, filter, foldLeft, etc. in terms of this new foreach, then you can throw new Break() out of any of those as well, which could be handy.
-- BrynKeller - 11 Jun 2003
Unlike foreach, those functions return a value. What would be the semantics of breaking inside those? For instance, what does
[1,2,3].foldLeft((int i, int j) => throw new Break(), 0) return?
-- DanielBonniot - 11 Jun 2003
Zero, I'd say. Actually, the current definition of foldLeft would already work:
<Any T, Any U> U foldLeft(java.util.List<T> l, (U, T)->U f, U init)
{
U res = init;
l.foreach(T elem => { res = f(res, elem); });
return res;
}
assuming that foreach looks something like this:
<T> foreach(c@java.util.Collection, f)
{
try
{
for (java.util.Iterator<T> i = c.iterator(); i.hasNext();)
f(i.next());
}
catch (Break b)
{
//Ignore
}
}
Oh, it seems everything's already implemented with foreach, so we should be all set:
-- BrynKeller - 11 Jun 2003
That would at least break the implementation of map on arrays (file arrays.nice):
<C,T,U> map(a@Array, f) = fill(new U[a.length], int i => f(a[i]));
If f is allowed to throw Break, then we cannot assume that the result will have the same size as the argument. We could of course adapt the implementation. My question is: is it useful, and clean, to allow to break during map, filter, ...
-- DanielBonniot - 12 Jun 2003
Hmm, that's a good point. I would say it's definitely useful - I seem to recall often wanting to break out of maps and filters in the past. Clean is another question - as you pointed out, handling Break in some cases could make things less efficient - for instance map for arrays would have to be written something like this:
which is clearly going to be slower than the current implementation. Is it worth it? I'm not sure. What if we kept foreach the way it is now, but added forbreak as a foreach where you could throw a Break , and then added new
methods like mapbreak, filterbreak and so on? Another approach that would be a little odd but would clutter the namespace less would be to define foreach like this:
<T> foreach(c@java.util.Collection, f, allowBreak = false)
{
if (allowBreak)
try
{
for (java.util.Iterator<T> i = c.iterator(); i.hasNext();)
f(i.next());
}
catch (Break b)
{
//Ignore
}
else
for (java.util.Iterator<T> i = c.iterator(); i.hasNext();)
f(i.next());
}
and then we could write other methods with allowBreak parameters too:
<C,T,U> map(a@Array, f, allowBreak)
{
if (allowBreak)
List<U> list = new ArrayList(); //or maybe LinkedList
list.foreach(T elem => { res.add(f(elem)); }, allowBreak = true);
return list.toArray();
else
return fill(new U[a.length], int i => f(a[i]));
}
but maybe this is all getting too far-fetched.
-- BrynKeller - 12 Jun 2003
These days, if you want this sort of thing, you just use the generator
library in nice.functional. If you want to break out of a map/filter/etc.
you just call stop() and that's equivalent to breaking. So:
import nice.functional;
let foo = naturals().filter(
int i => {if (i == 53) { stop(); } return odd(i);
).map(int i => i.toString);
gives you a generator which starts with all the naturals, filtered down to only the odd ones,
converted to strings. Except the method passed to filter calls stop() when it hits i == 53, so the whole chain ends at that point. It turned out to be a lot simpler than rewriting the entire collection hierarchy and so on.
-- BrynKeller - 02 Feb 2004
There are still reasons why we would want to modify the collection hierarchy (not necessarily the implementation, but make it have a different hierarchy structure) so that it would have no unsupported operations. That's a different project, of course.
-- DanielBonniot - 03 Feb 2004
An overview of the methods and function in stdlib for discussing semactics and as starting point of the documentation.
The documentation comments are before the signatures and discussing about sematics and implementation after the signature.
<Any T> !T[] elementsNotNull(?T[] arr);
/** Returns an array with the newSize first elements.
The result can be the same array as the argument.
*/
<Any T> T[] resize(T[], int newSize);
<T,U | T <: U> void copy(T[] from, int fromIndex = 0, U[] to, int toIndex = 0, int count);
<T,U | T <: U> void copy(List<T> from, int fromIndex = 0, U[] to, int toIndex = 0, int count);
<T, U | U <: T> T[] copy(U[] array);
<Any T> T[] slice(T[] array, int from = 0, int to = -1);
to is now inclusive maybe exclusive is better
/**
Fills a newly created array with non-null values.
A typical usage is to allocate a new array and set its values at the same time:
<code>
String[] numbers = fill(new String[10], int i => "number " + i);
</code>
The equivalent code in Java would be:
<code>
String[] numbers = new String[10];
for (int i = 0; i < 10; i++) {
numbers[i] = "number " + i;
}
</code>
It is important that no reference to the array is kept,
because that would make it possible to store null values in it.
There is no danger as long as the array is created inside the call,
like in the above example.
*/
<Any T, Any U | U <: T> U[] fill(T[] array, int->U value);
In particular, this is useful with T = ?X and U = !X.
The above type is more general, and useful when the component type is a type variable.
/** Returns an array containing, in order, the elements of both arguments.
The type of the elements of the arguments can be subtypes of the result
elements, since a new array is created.
*/
<T, T1, T2 | T1 <: T, T2 <: T> T[] concat(T1[] a1, T2[] a2);
<T, T1, T2 | T1 <: T, T2 <: T> T[] `+`(T1[] a1, T2[] a2);
<Any T> !T notNull(?T value);
/*
Allows to consider any nice object as an instance of java.lang.Object
*/
<Any T> Object object(T);
/** Unsafe cast operator. UNSAFE! */
<Any T, Any U> U cast(T) = inline nice.lang.inline.Nop();
String replace(String source, char c, String with);
String deleteAtEnd(String source, String what);
/**
* Break a string into a <code>List<String></code> of substrings,
* splitting at (and removing) every occurrence of the <code>separator</code>
* string.
*
* @param str the string to split
* @param separator the separator to split on
*/
List<String> split(String str, String separator)
requires separator.length() > 0 : "separator cannot be the empty string";
/**
* Join a collection of strings together, interspersing <code>separator</code>
* among them.
*
* @param strings the strings to join
* @param separator the separator string to place between each
* string and the one before it
*/
String join(Collection<String> strings, String separator);
/**
Count the number of elements for which test returns true
*/
<T> int count (Collection<T>, T->boolean test);
/**
@return true if all boolean in the list are true otherwise returns false
*/
boolean and (List<boolean>);
/**
@return true if the test returns true for all elements in the Collection otherwise return false
*/
<T> boolean all (Collection<T>, T->boolean test);
<T> T max(Collection<T> coll (T,T)->int comparator) requires !coll.isEmpty()
<T> T min(Collection<T> coll (T,T)->int comparator) requires !coll.isEmpty()
/**
Fold left without init value
*/
<T> T foldl(List<T> list, (T, T)->T func) requires !list.isEmpty();
/**
Fold right without init value
*/
<T> T foldr(List<T> list, (T, T)->T func) requires !list.isEmpty();
Erik Meijer and Wolfram Schulte have some interesting ideas about "stream types" which look like they could improve some of the code I've been writing. See Unifying Tables, Objects, and Documents. Since we already have the stream-like types T and ?T (which correspond to T! and T? in that paper) would it be possible to extend MLsub to provide T* and T+ types?
-- RohanHart - 21 Apr 2004
Good question, I'd be interested in that too.
-- BrynKeller - 22 Apr 2004
This page documents the current super mechanism in Nice. It compares it with super in Java, and calls for discussion on ehancements.
First, it does not make sense to just copy Java's semantics. In Java you write:
class B extends A
{
void m(int x) { super.m(x); ... }
}
So super in Java means roughly "fetch the following method in my superclass". With multi-methods this does not make sense, since methods consider all arguments to choose the implementation, not just the first one.
Another odity with Java is that you call call a different method than the current one, or pass it other arguments than the arguments of the current method. I think at least that calling the same method with the same arguments is by far the most common, so it should be more natural to do it, without having to reapeat the method name and arguments.
So the current definition in Nice is that super is an expression that calls the next implementation of the current method, with the same arguments. So the example would become:
class B extends A
{
m(x) { super; ... }
}
I would be interested to hear if other cases are useful in practice (calling a different method, or passing different arguments). These pose more theoretical problems. For instance the Dylan language says that passing different arguments is fine as long as they will result in the same implementation to be chosen, otherwise the result is undefined.
-- DanielBonniot - 12 Jul 2002
I have used super with different arguments in the case of constructors in Java. They are used to set default or constant values for the base class.
Could you give a price example?
-- ArthurSmyles - 10 Apr 2003
In an attempt to understand better the syntax part of nice, here is the class hierarchy for the bossa.syntax package. I have included questions and comments for discussion.
abstract public class Node;
class AST extends Node;
class Definition extends Node;
class AbstractInterfaceImplementation? extends Definition;
class CustomConstructor? extends UserOperator?; Why is this a UserOperator??
class NiceMethod? extends UserOperator?; Why is this a UserOperator??
abstract class MethodContainer? extends Definition;
class AbstractInterface? extends MethodContainer?;
class ClassDefinition? extends MethodContainer?; Rename proposal: AbstractClassDefinition?confusing because it can be read as AbstractClass?-Definition.
public static class ClassDefinition?.Interface extends ClassDefinition?Rename proposal: InterfaceDefinition?
public static class ClassDefinition?.Class extends ClassDefinition?; Rename proposal: ClassDefinition?
abstract class MethodImplementation? extends Definition implements Function;
class DefaultMethodImplementation? extends MethodImplementation?;
class MethodBodyDefinition? extends MethodImplementation?; The name of this class must be changed or the class removed. Can this be merged into MethodImplementation? itself?It shouldn't be merged. What's wrong with the name?The name is confusing because of the switch from definition to implementation, and back to definition. Is this a definition or implementation of a method? How is it different than the super class?
public static class FormalParameters?.NamedParameter extends FormalParameters?.Parameter
public static class OptionalParameter? extends NamedParameter?
public class Polytype extends Node; Why is this a node? Why is Monotype not a node?
public static class ClassDefinition?.ClassImplementation; Why are these not Definitions? Also, use of inner classes needs to be broken.Definitions are toplevel and ClassImplementation? is just a part of ClassDefinition?.
class JavaClass? extends ClassDefinition?.ClassImplementation;
abstract class NiceClass? extends ClassDefinition?.ClassImplementation;
class StatementExp? extends Expression; How can a statement be an expression? Example?A statment can be viewed as a void returning expression. Only used in IfExp? atm.
class MonotypeConstructor? extends Monotype; What is the relationship between a MonotypeConstructor? and a Monotype? Given that we already have Constructors, can this class be renamed?typeconstructor is a typesystem term
// Misceallaneous
class Arguments;
class Contract;
class LocatedString?;
final class ValueOverride?;
class ACatch; Why is this not a statement?It's a helper class for TryStmt? because a try can have multiple catches.
// Helpers:
class JavaClasses?
final class NiceUtils?;
final class TypeConstructors?;
class TypeParameters?;
class Info (analyse.nice)
// Other
interface Macro extends gnu.expr.Inlineable
interface Module extends mlsub.compilation.Module
interface Function;
// Exceptions
class DuplicateIdentEx? extends Exception;
class UnknownIdentException? extends bossa.util.UserError;
-- LucPerrin - 17 Nov 2004
To be most useful, the tests should be done very frequently. The best is thus to have use a machine that is turned on permanently, and connected to the network. That way, you can configure it to run the tests, for instance, every day at a certain time. Everyting will happen automatically.
It's also possible to install the test scipts on machine that are not turned on all the times. The tests can then either be run automatically when the machine is on (using for instance anancron), or manually, at special times, like shortly before a release.
To be able to send the results to the result page, you will need to have the rights to scp a file to the Nice project on sourceforge. As far as I know, this means being a member of the project. If you wish to set up a TestMachine, but are not a member of the project, contact DanielBonniot.
The following variables are needed by the script. They all get default values.
Variable
Meaning
Default value
user
Your user name on sourceforge
$USER (your user name on the local machine)
machine
A name identifying this machine
hostname -f
If you need to override them, you must create a file called setup in the main directory.
For instance, since my user name at home is daniel, I have a setup file with one line:
user="bonniot"
The config.name file.
This is the file that decides what versions of tools are used to run the tests. It is possible to have several, in which case the tests will be run several times. name will be used to identify each configuration. Choose it to reflect the main points of that configuration. At the moment you only need to configure JAVA_HOME.
For instance, I have a file config.jdk1.4 that contains the following line:
Let's check that your installation is correct. From the main directory, type:
./run
The scripts will download the latest version of Nice, and of several applications. Then it will try to bootstrap the compiler, and to use it to compile the applications.
If all goes well, you will have in the builds subdirectory the results of the testing, in the *.res files, and the logs of the build for each configuration in the *.log files. The script has also attempted to send them to sourceforge, where they will be processed to be included in the result page. That page is generated once every hour.
If there seems to be a setup problem, look at the builds/*.log files to see what happened.
This depend on your system. On unix, you can use crontab to configure when the tests should be run.
I use the following contab entry:
20 13 * * * (cd $HOME/OSS/tester; nice ./run)
Note that nice is used to give the build a low priority, so that it does not slow down my machine. It has nothing to do with Nice!
-- DanielBonniot - 26 Apr 2003
The Nice TestSuite is a framework to test the Nice compiler. For this aim, a proprietary file format for the testcases has been created. Here are some testcase examples:
/// COMMENT This testsuite tests the following ....
/// PASS
int a = 1;
/// PASS
/// TOPLEVEL
/* A simple global var. */
var int x = 0;
/// FAIL
/// COMMENT this should fail
int a = "";
/// PASS
/// package a
/// TOPLEVEL
class A
{
int x = f();
}
int f() = 0;
/// package b import a
;
TestCase? keywords are all prefixed with ///. On root level (without any whitespace indentation) currently following keywords are allowed
PASS and FAIL
The keywords PASS and FAIL indicate a testcase on its own, and whether the expected behaviour while compilation is pass or fail. Testcases cannot reference each other, they are all compiled and tested independently.
When a bug is found, I often write a testcase for it first, before the bug is fixed. In that case, it is useful to flag the testcase as being a known bug. This is done by adding bug at the end of the line.
I such a testcase is indeed found to include an error, well we knew it and print nothing. But if it works, then print a victorious message! :-). In the summary:
known bugs: 2
fixed: 1
Testcases may contain the TOPLEVEL keyword that has the following meaning: everything in front of the TOPLEVEL keyword will be collected in the main() method. Statements behind the TOPLEVEL keyword are global to the package, like global variables, functions, methods and class definitions.
PACKAGE
The PACKAGE keyword indicates which package the sources should belong to. More than one PACKAGE keyword can be declared to build a testcase with a certain package structure. Packages can be imported by the PACKAGE some IMPORT other keywords.
After the GLOBAL keyword source code can be written that is accessable by all following testcases. Global sources can be placed at where (before, after or between testcases) in the testsuite file. The scope of global sources is restricted to the same testsuite file and to testcases that are behind the global source. Globals can appear more than one times in the file as the next example demonstrates
/// GLOBAL
int g1 = 1
/// PASS
/* access to g1 only */
int a = 1;
/// GLOBAL
int g2 = 2
/// PASS
/* access to g1 and g2 */
/// TOPLEVEL
/* A simple global var. */
var int x = 0;
Because globals are accessible only in the same file, placing global sources at the end of the file makes no sense.
Comments can be included, and if desired outputted, at any place in a testsuite. Comments have the following syntax
/// COMMENT place your comments here
Comments are one-liners, if you want to write a long comment, write the whole comment in one line as described above or break it into more COMMENT keywords, each in a new line. The following example shows possible ways to comment
/// COMMENT This testsuite describes ....
/// GLOBAL
...
/// PASS
/// COMMENT This testcase demonstartes ...
/// COMMENT ... line two of testcase comment ...
...
/// COMMENT here we could comment the end of the testsuite :)
Activating comments in the output can be done by specifying the -comment flag in the command line
java -classpath /usr/share/java/nice.jar nice.tools.testsuite.TestNice ~/testsuite/ -comment
or
java -classpath /usr/share/java/nice.jar nice.tools.testsuite.TestNice -comment ~/testsuite/
While running the testcases temporary files and folders are created in "testsuite-temp-folder". Testcases that fail, in the sense of that they don't behave like expected (pass or fail) are collected in the folder "testsuite-fail-folder" for later investigation purposes. In this folder, each testcase is grouped in its own folder that is numbered from "1" on in ascending order. At the next testsuite run both folders are initially cleared. -- AlexGreif? - 27 Jun 2002
In a /// FAIL test, it is often desirable to specify where (which line, which column) the error should be reported. This allows for checking that the compiler gives the good location to the user, and also that the test does not fail "by accident", for an other unrelated reason (for instance a syntax error when writing the test).
This can be done, by embedding a comment of the form
/* /// FAIL HERE */ just before the location of the desired error in the test source.
Warnings that result from wrong FAIL HERE positions are treated sort of failures. Thus the source code is shown and the compiler results.
If you use Java 1.4 or later, use java -ea instead of java -Dassertions=true (this should also be done in the Makefile, but how do we make it work for both 1.3 and 1.4?).
When a test fails, it would be useful if the output of the compilation (and the output of the run, with the stack trace of the exception thrown) was saved in a file, in the failure directory.
Sometimes I want to look at the code generated by succesful tests. It would be good to have a -save comment line option to say to save all tests, not just the failing ones.
Well, that's all for now! Quite a lot of new features :-) None is critical, so do what you can when you find the time. But all these features will make testing easier.
We need to track compiler warning, not just errors. I think by default a warning should be considered as an error. We could add a ///WARN tag to say that a test should (or can?) emit a warning.
(Not urgent: I'm not sure if there is any interesting warning issued by the current compiler).
At the moment, the testsuite engine emits a warning if there was no error where you expected one. It should also warn if there was an error where you did not expect any. Probably, as an exception, a test case that has no explicit FAIL HERE is also OK.
The rationale is to test cases, like the one I just solved, where one
error is expected, but the compiler reports two errors.
It could be useful to define some macros, in the form of /// keywords, for tests that are repeated many times with a similar pattern.
One example is:
/// TYPE Type1 SUBTYPE OF Type2
In the engine, this would be expanded to some Nice source that will compile if and only if Type1 is a subtype of Type2.
This is not urgent. If we really need something like this, perhaps it would be wiser (and more fun!) to transition the testsuite to a set of Nice functions, so that test cases could be written in Nice itself.
An interresting idea from the Groovy language is to have an easy way to create tree like data as xml, html and user interfaces.
More info: http://groovy.codehaus.org/markup.html
-- ArjanB - 08 Jan 2004
Erik Meijer using declaritive XML markup for GUIs inside a program and using declaritive XML markup for arbitrary types
public class Contact {
sequence {
string Name;
string? Email;
}
}
public class Test {
static void Main() {
Contacts cs = <Contacts>
<Contact>
<Name>Erik Meijer</Name>
<Email>{"emeijer"
+"@"+
"microsoft.com"}
</Email>
</Contact>
<Contact>
<Name>Dare Obasanjo</Name>
</Contact>
</Contacts>;
...
}
}
-- IsaacGouy - 09 Jan 2004
I like the idea of a language that understands xml and can express and check types for it. But imho xml literals are just ugly. Humans should not have to grok XML
Though most things(like gui's) could be written in some xml format, it's better to have some syntax that's more general and easier to read.
-- ArjanB - 09 Jan 2004
Never wanted to read much XML myself. OTOH when I read about Groovy support for various markup languages from XML, HTML it seems reasonable to represent snippets of XML by... well, snippets of XML.
That seems more obvious and more general.
-- IsaacGouy - 10 Jan 2004
Speaking from experience from working with Groovy, Groovy markup is absolutely wonderful for some things, But sometimes,
it's even more awkward to write XML tree constructors in function-invocation syntax than in XML syntax. It can be very annoying.
Using XML in-place can be terrific - for a great example, see Scala.
-- MarkCC - 03 Jan 2005
This page describes the feature request to use tuple types in function declarations and anonymous functions.
The following code shows the currently working way.
The new feature is commented out.
-- AlexGreif? - 05 Mar 2003
I agree it would be practical to be able to name the components of the tuple in its type declaration. This is actually already possible for a local declaration, which you use in the first function. But it needs to be implemented for function declarations.
We also have to disambiguate with the first case, by putting additional brackets:
((String letter, String number)) => ...
-- DanielBonniot?
This feature is now implemented (in the CVS-tree and will be in the 0.9.0 release) -- ArjanB - 16 Jun 2003
Arjan has mentioned this a couple of times.
Given that Nice is more than an OO language, do we need more than the ability to define a new object?
(On a related subject, maybe it's worth (no pun intended) mentioning that subrange types were removed from Modula-2 in the transition to Oberon-2, too complicated with too little benefit. Probably worth considering subranges separately.)
see alsoCheckedIntegerArithmetic
-- IsaacGouy - 31 Dec 2003
The purpose of a TypeAlias is improve readability by being able to replace long types which are used in multiple places in the source code. Making a subclass or interface is not always possible because that can have semantic side-effects.
Thingy = Map<Foo<Bar>, List<String>>
MyFunction = (List<Foo>, Bar, int, String)->int
Another advantage is that TypeAlias could make explorative programming easier. When you don't know yet which type to use, you can use a TypeAlias so that changing the type don't require that much changes all over the place anymore.
-- ArjanB - 01 Jan 2004
And 'Thingy' & 'MyFunction' showing up in error messages?
-- IsaacGouy - 03 Jan 2004
Preferably but it will be quite hard to implement -- ArjanB - 04 Jan 2004
Generics are implemented in Nice using a technique called `type erasure' - just like in Java 5.
The technique and some of its repercussions are quite nicely described at: http://gafter.blogspot.com/2004/09/puzzling-through-erasure-answer.html
Basically, choosing for type erasure means the knowledge which type parameter was used is known at compile-time, but not anymore at runtime. That is why you can do `foo instanceof NiceVector?', but not `foo instanceof NiceVector?<String>'.
Also, casting a NiceVector?<Integer> to NiceVector?<String> will not give a ClassCastException?. This means you can add strings to a (casted) NiceVector?<Integer>, and you'll find out only when you try to remove that element from the NiceVector?<Integer> as an Integer. In other words, you'll get a ClassCastException? in another place then where the bug is, and in fact in a piece of the code where there is no cast.
-- Main.Raboof - 30 Apr 2005
I'd like to propose something that, at first glance, goes against the strongly typed phylosophy of Nice and Java. I'd like to have the ability to sometimes have a variable that does not have a known type, at compile time. This would mean that the compiler should make no assumptions on the variable's type. It would also mean that these variables would have to be resolved at runtime, for access to their fields and methods.
In Java or Nice today, we are forced to get by using an Object type, when we don't or can't know the type. The main problem with using Object is all of the type casting and instanceof operators that must be used with these variables or members.
Since Nice has MultiMethods?, meaning that methods are found dynamically, it only seem logical that this also be possible to accomplish at runtime, rather than compile time.
We would add unknown (and ?unknown) as a primitive (built-in) type. Here are some examples of the use of unknown:
unknown depthSearch(Tree tree, unknown find);
unknown entry = "This is a string";
?unknown first = null;
unknown[] list1 = new unknown[50];
var list2 = new ArrayList<unknown>();
int len = entry.length(); // works because entry currently refers to a String and because
// String.length() returns an int
file = new File(entry); // works because File has a Sring constructor, and without typecasting
int width = entry.width(); // would compile, but fail at runtime
Set<int> set = entry.charAt(0); // seems silly, but would compile, then fail at runtime
Most scripting languages like Perl, Python, Ruby, and JavaScript never declare types for variables. Here are some of the advantages of unknown variables.
Variables may refer to diverse objects
The programmer doesn't need to know what objects types will eventually be used
The actual type will be known at runtime, so correct methods will be looked up and called
No type casting is needed to pass these objects to strictly typed methods
No type casting is needed to call methods or access fields of unknown variables
May hold anything: primitives (built-ins), arrays, or Objects
Allows programmers to add scripting capabilities or hooks to their applications
There are some problems with untyped variables, which is why many of us favor strongly typed languages for enterprise level projects. Because of these problems, the use of untyped variables should not be easy or encouraged, as they are in scripting languages. Here are a few of the problems.
Slower access because of runtime lookups. This could be improved by the compiler in 'if' statements as follows:
unknown found = list2[15];
if (found instanceof Number) {
/* Compiler knows 'found' is a Number here and can take advantage of that */
}
More potential runtime errors. Theses are inevitable, because the compiler can't help much.
Complex and tedious to implement in Nice. I'm guessing here. But I would bet that since Nice is strongly typed, adding the unknown type will cause lots changes and ripples throughout the compiler. I still think they are worth the effort, however.
Fields of unknown objects must also all be considered as type unknown. This is because at compile time we can't know if fields will exist or their types. This may also be the case for the return types of methods called on _unknown_types.
See also DynamicMembers for an idea of how to possibly implement runtime member lookup.
-- TroyHeninger - 25 Oct 2003
Thanks for your ideas. It is true that although strong typing is better when it is possible (and with Nice we try to make it possible more often), there will always be cases where it is not "clever" enough.
One aspect of Nice you might have missed is that, with type parameters, you can handle some of the situations you mention. For instance, instead of
unknown `.`(row@Results, String column);
you can already write:
<T> T `.`(row@Results, String column);
which says: the return type is T, for whatever value of T you choose (since T does not appear in the parameter list).
This is already used in the standard library for serialization/deserialization without casts. You can also look at this forum thread about reflexion support.
This does not allow to have a local variable of any type, though, because T must be declared in a method type. But I believe this already covers many interesting cases.
-- DanielBonniot - 25 Oct 2003
That brings up several questions. 1) Does Nice currently support overriding the `.` operator, as you implied above? If so, then you've already answered my request for this in DynamicMembers. 2) With Nice's current type capabilities, is it possible to create an array or tuple of unknown types? I don't think it is. 3) How hard would it really be to add unknown to Nice? If you don't think it would be too hard, I'd be willing to look into adding it myself. I do have compiler writing experience, though I don't have tons of time.
-- TroyHeninger - 12 Nov 2003
1) You can put anything inside ``, so a method definition using `.` is valid. However, you would not be able to call it using the e1.e2 syntax, so that's not yet what you want (in the other discussion). My point here was about typing.
2) You're right, using <T> for unknown types does not carry on to arrays. What you can do currently is use Object for that purpose. You can convert any value to Object by using the method object(...). On the way back, you would use a cast orinstanceof.
3) Basic support exists, using Object as described above. One could do better, by not requiring the use of object(...) (that is, make Object the top type). This should not be very difficult.
If you want to allow any operation on Object (or unknown) to succeed, and discover the operation at runtime, that would more work. Although maybe you could just rewrite f(e1, ..., eN) where at least one of e1...eN has the unknown type into dynamic_call("f", e1, ..., eN), and implement dynamic_call as a library function that uses reflexion and the class of its arguments to find what method to call. Is this what you are driving to?
It's interesting, although my bias is usually more towards static solutions. I'm not sure if you are awre that Nice is already quite clever about instanceof, so you don't need casts in addition:
Object f = operationReturningAnything();
if (f instanceof String) {
// f has type String here, no cast required.
int len = f.length(); // String operation, resolved at compile time.
}
This removes about half of the boilerplate needed in Java when using arbitrary types. If the removal of object(...) mentioned above was implemented, would not this provide quite a good support already, without loosing any type safety?
-- DanielBonniot
These are aspects of the language that are not covered by the User Manual, but that should be.
overview short paragraph giving the big information - methods not contained in classes or interfaces; classes holders of state not methods; packages encapsulate state, type, methods -- IsaacGouy - 12 Feb 2004
Additional type in patterns to resolve ambiguities: ident@type(additionaltype)
Treat ints as bit arrays (this is defined in nice.lang, not sure it belongs in the manual)
|| operator with nulls (idem, defined in nice.lang)
Type inference (variables, for loops, null-tests, instanceof narrowing)
Array type syntax (with option types)
Function type syntax (with option types)
Explanations of manual formatting conventions (for syntax examples and so on)
Package Methods (which used to be toplevel methods) the section on 'Local Methods' refers to them but I can't find anything else in the manual that explains them. IsaacGouy
References to Methods (maybe this is the same as function type syntax?) Explain how we get a reference to a package/local method and an object method let String->String f = toLowerCase; [Developer Guide not Manual] IsaacGouy
Extending, implementing classes and difference between implements and finally implements
Autoboxing
Super calls
Expression local variables
Java differences no switch statement - how do you do that in Nice (value dispatch, nested if) [Developer Guide not Manual]
How do you interoperate with Java code that relies on features Nice doesn't have (inner classes) [Developer Guide not Manual] -- IsaacGouy - 08 Feb 2004 (Answer for inner classes: nothing special, you can specify the name as ToplevelClass.InnerClass)
Manual doesn't say that abstract interfaces are not inherited
The requirement of using fully qualified name for the class at abstract interface implementations.
Exceptions.
Aspects that should be updated:
Range expressions - metioned in for loop section, should have a section in expressions chap.
Classes - initializers, overridable finals
Retypings - it needs more explaination and more kinds of retypings exist(constructors, fields, static methods)
!T vs. T clarification, as discussed on the Nice-Info list. Include definition of 'subtype' in Nice.
The documentation for || with nulls should explain the interaction between || and autoboxing. In particular, 1 || 2 seems to be the same as new Integer(1) || new Integer(2). (The compiler should also be able to detected the dead code in this situation: new Integer(1) can never be null, so new Integer(2) is never executed.) Also, the documentation should explain the way that || coexists with the nullness checking for fields as demonstrated at the end of this example:
class A { ?Integer n; }
void main(String [] args) {
println(0 || 1);
println(2 || 1);
println(null || new Integer(1));
println(new Integer(2) || new Integer(1));
let a1 = new A(n: null),
a2 = new A(n: new Integer(2));
println(a1.n || new Integer(1));
println(a2.n || new Integer(1));
Integer n1 = a2.n || new Integer(1);
println(n1);
// NOT equivalent to
// n2 = a2.n || new Integer(1);
// error:
// The value if(`!=`(a2.n(), null))
// a2.n()
// else
// new Integer(1)
// cannot be assigned to n2 because it might be null.
//
// Integer n2 = a2.n != null ? a2.n : new Integer(1);
// println(n2);
// equivalent to
// n3 = a2.n || new Integer(1);
?Integer temp = a2.n;
Integer n3 = temp != null ? temp : new Integer(1);
println(n3);
}
Output:
0
2
1
2
1
2
2
2
Why do you use Integer as type instead of int?
What made you trying out all kinds of variations with the || operator?
I don't think users need to know about how autoboxing happens.
-- ArjanB - 29 Jan 2004
It was mentioned in the ConstructorSyntax discussion I believe. I tried it out because it wasn't documented and I wanted to figure out the semantics. I think it is important to document the semantics explicitly. But I think that I may be wrong about autoboxing anyway; it is also not documented and I haven't been able to figure out the autoboxing semantics yet. Anyway, I think that operator || is simply mistyped and should not apply to int/byte/long/float/double since it doesn't make any sense for numeric types. Maybe the typing should be:
That is, somehow require that the first parameter be an option (nullable) type. The Nicec compiler doesn't accept this syntax but maybe there is another way?
-- BrianSmith - 29 Jan 2004
The OptionOr? can make sense for numeric types too, example:
Map<String, int> map = new HashMap();
map["abc"] = 5;
int n1 = map["xyz"] || -1;
int n2 = map["abc"] || -1;
println(n1); //prints -1
println(n2); //prints 5
It's not possible to give OptionOr? a type so that it requires the first parameter to be an option type because of the typechecking rules for function application. But OptionOr? is an inlined operator so it would be possible to generate a warning when using it with a non option type first argument.
-- ArjanB - 29 Jan 2004
|| is a method defined in nice.lang, so I don't think it should be documented in the UserManual?. As soon as we have a working nicedoc, we'll publish the documentation for the standard libraries on the website.
The general question is to document when autoboxing happens. On the one hand, one can often see that as an implementation detail, but either when you use reference equality or for performance reasons, it is sometimes important to know. So yes, that should be better documented. I'm not sure if it should go in the UserManual?, or if we should have a different document for lower-level aspects like this (this does not really belong to the definition of Nice itself, it just happens to work like this when you compile to the JVM).
The rule is that when a primitive value is used when a generic type (T) or an Object is expected, the value is boxed.
Unboxing happens as need when a boxed value is used where a primitive type is expected.
-- DanielBonniot - 30 Jan 2004
The documentation mentions multi-line strings but it would be clearer to show how they would map to traditional syntax. For example, it is not clear how whitespace at the beginnning/end of an internal line works in a multi-line string.
let greeting = """
Hello, world.
You may be thinking, \"Why was I called here today?\"
Well, there was a good reason. Honest.
""";
Is it equivalent to:
let greeting = "Hello, world. "
+ "You may be thinking, \"Why was I called here today?\" "
+ "Well, there was a good reason. Honest.";
or:
let greeting = "Hello, world.\n"
+ "You may be thinking, \"Why was I called here today?\"\n"
+ "Well, there was a good reason. Honest.";
or something else?
Hmmm maybe there's something to be said for being explicit, my guess would have been
let greeting = "\nHello, world.\n"
+ "You may be thinking, \"Why was I called here today?\"\n"
+ "Well, there was a good reason. Honest.\n";
What should be the exact semantics of the VisibilityModifiers?
Can VisibilityModifiers added to method declarations or method implementations or both?
How do VisibilityModifiers affect where you can implement and/or call methods?
I have thought about this but I didn't get consistent and usefull semantics.
The only easy ones are the global constants.
Anyone some good ideas?
-- ArjanB - 09 May 2003
It's also easy for functions (unless we have FunctionsReplacedByMultiMethods, in which case the question disappears).
For methods, I think that visibility only makes sense to method declaration. It should restrict the places from where you can call the method.
I don't think that we can/should restrict the places you can implement the method, because I believe that in some situations you would need to implement a method where it is not accessible.
Similarly for classes, visibility should not restrict implementing a method for that class. I can see two usages for non-public classes. One is to make the constructor restricted, which means you cannot instanciate them outside the scope. The other is that you cannot use them in types. The first one is surely needed (singleton pattern). I'm not sure about the second. A possibility is that both would be restricted.
I think the second one is too restrictive to be useful in practise.
- DanielBonniot - 10 May 2003
The simplest solution is to make VisibilityModifiers only affect the places where you can call a method/constructor.
I think there should also be a way to restrict the places where you can implement a method.
Implementing a simple form of the VisibilityModifiers first and using them will give more insight in what kind of additional restrictions are needed.
-- ArjanB - 03 Jun 2003
Agreed. I thought that a good way to start implementing this would be to develop an independant library for managing symbols with visibility properties. That would help keeping the complexity out of the compiler, and thinking abstractly.
It should probably be implemented in Nice, but keep in mind might be called by some Java code.
Anybody feels like working on this?
One feature that I find useful is when "invisible" symbols are not completely ignored, but the system can tell you "foo is defined in package bar, but it is not public".
Something to keep in mind is that we might want to have different visibility for the same symbol in different contexts (ex: a field that is public when reading it, but private when writing it). That could come in a second phase, though.
-- DanielBonniot - 04 Jun 2003
In InnerClasses, I mentioned that I use inner classes in Java to emulate Eiffel's selective export language feature. ArjanB? mentioned a "proposal is to give sourcefile access to things with the private modifier." I'm not sure how this works with methods of a class being defined in multiple files, multiple classes methods' being defined in one file, etc., so some of my concerns below may be irrelevent.
I think making all the classes in a single source file the equivalent of friends would be a mistake. It removes nearly all the benefit of visibility modifiers for classes inside one source file, which would end up encouraging a more Java-like one class per file. It makes the most common case difficult in favour of making the less common easy (if not too easy.)
It would also be far coarser grained than an Eiffel-like selective export, becoming all-or-nothing. You could not restrict access to a subset of methods, but would have to either give access to everything or everyone access. For example, you want to give class B access to method Z of class A. To achieve this with the friend-like proposal, you either give class B access to everything of class A, or give all classes access to method Z. Neither choice is ideal. With selective export, you could just list class B in the export list for method Z, instead of specifying public or private.
Further, it makes specialization difficult. By specifically restricting to classes within the same source file it prevents subclasses defined elsewhere (which is how package, friend, and inner-classes work, I don't know about the Nice proposal) from achieving the same degree of flexibility. With selective export, you could create a class C subclassed from class B, and it would still have access to method Z. With package/friend/inner classes, you'd have to actually define class C in the same source file as class B, which is impractical and often impossible (e.g., class B is in a library.)
Selective export is a wonderfully usefull feature, but is all about finesse and fine control. Emulating it with package/friend/etc. is destructive overkill. I'd love to see Nice gain this advantage over Java. I'm not sure how they would interact with multi-methods though.
I must also say that I find it a little unsettling that units of storage, the source file, are being given semantic meaning in the language. While certainly convenient to the language designer, I think it makes the language a bit inflexible.
-- Vulcannis - 28 Aug 2003
The main problem of visibility in nice are multimethods, they do not belong to a class so defining usefull semantics is difficult. I haven't seen any other language with multimethods that has visibility restriction.
For acces related to subclasses as protected in java I see no way to give them clean semantics.
Besides restricting where a method can be called we need a way to restrict where method implementations can be added.
Visibility remains an open issue for now, probably it will be implemented in many small steps to find out a good balance between control and flexibility.
I will take a look at eiffel for hopefully some good ideas.
-- ArjanB - 28 Aug 2003
I'm going to consider only class or package member (field and method) access from functionals (multimethods or functions) and ignore class visibility. Here's a diagram of Java's visibility rules for accessing. At each functional implementation location relative to the member we're accessing, all members with any visibility below, to the right, equal to the table position are accessible:
Location
same class
subtype of class
anywhere
same file
private
same package
(package)
anywhere
protected
public
From the above we get two chains of inclusion: public > package > private, and public > protected > private, where > means “is more accessible than.” It's possible to apply the exact same visibility rules to Nice. Using these rules would be a good place to start.
The rules for accessing private members could be relaxed. Private members could be permitted to be accessed by any functional, as long as they appear in the same file. Since Nice functionals aren't very strongly linked to classes, I favor this approach. The table is displayed below. At each location relative to the member we're accessing, all members with any visibility below, to the right, equal to to the table position are accessible:
Location
same class
subtype of class
anywhere
same file
private/protected
same package
(package)
anywhere
protected
public
Discarding protected would simplify the above table: private would mean “this file only,” and package and public are self-evident (protected is not currently even a recognized keyword in 0.9.1). However some design patterns, e.g. Template Method, rely on protected. I think protected can be implemented for multimethods: a given method implentation should have access to protected members of any objects that are passed to it via dispatch.
It should definitely be possible to specify different visibilities for reading and writing fields. Trouble is, there are a lot of combinations of visibility, even with the restriction that field set must be less visible than field get. Something like public(read) private(write) int x; or public(get) private(set) float y are very clear, but verbose. Hyphenated words might work: public-private, public-read (package write), and private-write (package read) cover all the cases that don't involve protected.
A side note: it's also possible to provide support for rare cases where accessing a hidden member may be desirable. AspectJ provides the privileged keyword for this (http://dev.eclipse.org/viewcvs/indextech.cgi/~checkout~/aspectj-home/doc/progguide/apbs05.html#d0e6222). It applies to an entire aspect, and grants that aspect the ability access to all members. If privileged is deemed useful for inclusion in Nice, I think it should apply at the functional implementation point instead of the class level, but the idea is still the same.
-- JohnBMesserly - 29 Aug 2003
Nice seems to have more than the usual design constraints, because of the expectation that Java classes will be extensively reused in Nice code. In the MethcallIntermediateExample, the java class in JavaToggleExample was written with the expectation that subclasses could access protected fields - that is not the case in Nice 0.9.2
The handling of protected java fields/methods is a known problem that needs to be fixed. -- ArjanB
-- IsaacGouy - 17 Sep 2003
I agree with Daniel that for methods, visibility only makes sense for the method declarations and should affect (only) the places you can call the method. But, I agree with Arjan that there should be a way to prevent specializations of a method from outside the package it is defined in.
It makes sense that a class's visibility would affect the ability to implement a method for that class. For example, if a class C is private to a package a.b.c, it should not be possible to define a multimethod that has an a.b.c.C argument or return type outside of package a.b.c. Similarly, it should not be possible to define a public method M in package a.b.c that is declared to accept parameters of type a.b.c.X or returns type a.b.c.X.
The visibility of a class's constructor should be at least as restrictive as the class's visibility. For example, it doesn't make sense to have a public constructor for a package-private class. However, it does make sense to have a package-private constructor for a public class. It might be useful to have protected for constructors--A protected constructor of class X can only be used by a constructor for a subclass of X. Then you could also have package protected which would allow use of the constructor by a subclass of X as well as by any code defined in the same package as X.
Beyond constructors, I don't think any modifier giving subclasses special privileges is very useful. Presumably, it would only apply to the classes' initializer and the default value expression for its fields.
I agree with Vulcannis when he says that it is "unsettling that units of storage, the source file, are being given semantic meaning in the language." But at the same time it does seem useful to have source-file level visibility as long as a single package can be spread across multiple source files. Perhaps a good way of handling this would be to have a package visibility modifier instead of private. A symbol (class, method, field, value) without a visibility modifier would be visibile only within the source file it is declared in. To grant access to other source files in the package, it must be marked with a package modifier. To grant access to other packages, it must be marked with a public modifier. Then you don't need a private to mean "this source file only" at all.
It seems like selective export like in Eiffel seems like it can only apply at the package level since methods are seperate from classes. E.g. package A could export symbols f, A, B.c to packages B and C. I am not sure how useful it is.
One important aspect is what the default visibilities should be. My preference is to make everything source-file-private by default. My second preference is to require a visibility modifier for every package-level construct.
-- BrianSmith - 04 Feb 2004
When defining visibility semantics is the assumption that they will apply to well behaved code or must they have runtime enforcement? The latter is likely preferable in real world applications yet may limit the semantics such that they can be implemented with those supplied by the JVM.
Note that Nice also has a form of capability security which is entirely missing from Java - local functions. Extending these to multidispatch LocalMethods? allows for methods private to the enclosing method or explicitly passed to outside parties. What forms of reflection on those method's environments should be allowed is a separate issue. It's almost like defining a method-based rather than class-based object system. Doesn't seem much scope for inheritance either, though functional composition...
-- RohanHart - 12 Jan 2005
void return types are allowed, and void can be used in anonymous functions. However ()-> still shows up in some error messages. Given the prevalence of C/Java would it be reasonable to consistently use void instead of (), and deprecate use of () ?
-- IsaacGouy - 14 Jan 2004
Statistics for Dev Web Month: Topic views: Topic saves: File uploads: Most popular topic views: Top contributors for topic save and uploads: Aug 2005 1736 0 0 116 ...
These are aspects of the language that are not covered by the User Manual, but that should be. overview short paragraph giving the big information methods not contained ...
TOC Documentation for the standard library We need a documentation for the standard library. The best way would be to write a tool NiceDoc, similar to javadoc. There ...
It would be nice to have a tool to convert a set of Java classes to Nice syntax (e.g. stripping out the typecasts, putting argument names in all method calls, etc ...
This page is where you can propose new features for Nice. page. You can also edit a feature page to comment on the proposal. PropertySyntax InnerClasses PartialApplicationSyntax ...
Generics are implemented in Nice using a technique called `type erasure' just like in Java 5. The technique and some of its repercussions are quite nicely described ...
Implementation issues of the compiler. TodoList BadErrorMessages Documentation CompilationAPI SyntanticClassHierarchy CompilerTerminology TypeErasureLimitations Building ...
http://sourceforge.net/tracker/index.php?func detail aid 671444 group id 12788 atid 362788 Section "Non-local variables" in http://nice.sourceforge.net/cgi-bin/twiki ...
Requirements: A proper javac for example the Sun or IBM ones. (not the gcj-wrapper) On Debian, for example use the `java-package' package Get the Nice module from ...
Before a new version is released, it is sometimes available for testing purposes. List of changes You can see the latest list of changes at http://cvs.sourceforge ...
The current behaviour of numeric type is to silently overflow. In some cases this is what is needed, and in some cases you don't really care, because it is guaranteed ...
Nice will get enums soon but first the exact implementation and syntax needs to be discussed. TOC Specification A good starting point is the java 1.5 enum proposal ...
What should be the exact semantics of the VisibilityModifiers? Can VisibilityModifiers added to method declarations or method implementations or both? How do VisibilityModifiers ...
On this page the Nice Eclipse Plug-in is described TOC What is it? The "Nice Eclipse Plugin" is a plugin for the open source Eclipse IDE. It is possible to extend ...
An interresting idea from the Groovy language is to have an easy way to create tree like data as xml, html and user interfaces. More info: http://groovy.codehaus ...
Maybe a year from now, one of the most obvious special things about Nice will turn up in releases of Java and C# ( http://msdn.microsoft.com/msdnmag/issues/03/10 ...
This is the place to discuss the ongoing development of Nice. FeatureProposals CurrentDiscussions NiceCompiler ToolsAndLibraries (includes EclipsePlugin...) DevelopmentVersion ...
As I am just starting to develop a new Eclipse Plugin for Nice, I wanted to share my first ideas and goals concerning features and their realization with the community ...
Current projects Doc.NiceSwing TestSuite EclipsePlugin NewEclipsePlugin Future projects (waiting for volunteers) NiceDoc NiceUnit syntax highlighting for vim update ...
In an attempt to understand better the syntax part of nice, here is the class hierarchy for the bossa.syntax package. I have included questions and comments for discussion ...
Terms that may need explanation related to compilers and typesystems or terms that have specific meaning for Nice. General arity closure formalparameter Types monotype ...
I was just wondering, when Nice interacts heavily with Java, the resultant code contains too many curly braces which are not nice at all, as exceptions must be handled ...
This is a draft about how package repositories will be handled. Some motivation for this feature is given Doc.GetInvolved#The Community there . TOC Principle A repository ...
I've started a JEdit syntax mode for Nice. Installation info and downloads are available from http://www.xoltar.org/2004/aug/05/jedit-nice-mode.html on my website ...
How and where to announce new releases? The release itself is done on sourceforge. It includes release notes and the changelog (copy-pasted from the http://cvs.sourceforge ...
Erik Meijer and Wolfram Schulte have some interesting ideas about "stream types" which look like they could improve some of the code I've been writing. See http: ...
First some simple examples how JNI in JAVA works: Test.java: class Test { static { System.loadLibrary("test"); } public static void main(String args) { System.out ...
Discussions of the language aspects of Nice. StandardLibrary EnumImplementation VisibilityModifiers NiceConstructors ClassInvariants PatternSyntax SingletonClasses ...
It would be good to allow a different syntax for method application in some circumstances. Specifically, it would be nice if: foo { baz(); } could expand into foo ...
Imported from SourceForge RFE #672069 Background: Many functional languages provide "curried" functions, of the form a b c, rather than the more typical (in Nice ...
A few syntax changes are being considered now. Using the same symbol for function types and anonymous functions. Making "val" an alternative for "let". "val" would ...
I'm trying to summarize my ideas on constructors, based on the NiceConstructors discussions we had. I think we should separate two aspects: creation of new instances ...
Construction is the process of creating a new object whose fields have some meaningful value. Currently, only the automaticly generated default constructor does that ...
Links to proposals UPDATE: As a first step, in the current development version, classes can have initializers, like in Java. One further improvement is to allow CustomConstructors ...
An overview of error messages that needs improvement. TOC To do Unsatisfiable constraints class A{} class B extends A{} interface I{} class X implements I{}
This is a subscription service to be automatically notified by e-mail when topics change in this Dev web. This is a convenient service, so you do not have to come ...
void return types are allowed, and void can be used in anonymous functions. However ()- still shows up in some error messages. Given the prevalence of C/Java would ...
Arjan has mentioned this a couple of times. Given that Nice is more than an OO language, do we need more than the ability to define a new object? (On a related subject ...
This page documents the support of the type Object in Nice, which has been present since version 0.9.5 Current Situation Object is currently a sort of alien in Nice ...
On a longer term(post 1.0) Nice might get a const keyword. Anyone who's is interested in writing a proposal should look at this proposal for java: http://david.tribble ...
Dynamic Members This is the ability for a class to add members to their class at runtime. Actually, that's not what I'm proposing. I really want classes to be able ...
I was thinking if it maybe would be a good idea to let the compiler help when writing properties. What if we could let the compiler automatically create get/set functions ...
Unknown Type I'd like to propose something that, at first glance, goes against the strongly typed phylosophy of Nice and Java. I'd like to have the ability to sometimes ...
We already have a comprehensive TestSuite, to check the correctness of the compiler. It would also be very useful to be able to automatically mesure performance. ...
Note: this has been implemented, as of Nice 0.9.2 Main.DanielBonniot 18 Oct 2003 Imported email discussion: Could there be a way to also be able to separate the ...
Overview of the things left to do in random order Features VisibilityModifiers. ChangeNumberOfTypeParametersInSubclasses PropertySyntax PartialApplicationSyntax Improved ...
We need a javadoc-like tool for Nice. If possible, it would be good to re-use someone else's existing backends and just write a frontend for Nice. Main.BrynKeller ...
I was wondering if it will sometime be possible to declare inner classes in Nice ? I guess this is very useful in some cases. Inner classes are currently not accepted ...
Does "Modification of the type system for subclasses with a different number of type parameters than the parent." mean that I could do: class B extends A {} rather ...
An overview of the methods and function in stdlib for discussing semactics and as starting point of the documentation. The documentation comments are before the signatures ...
On a longer term(post 1.0) Nice might get a const keyword. Anyone who's is interested in writing a proposal should look at this proposal for java: http://david.tribble ...
How and where to announce new releases? The release itself is done on sourceforge. It includes release notes and the changelog (copy-pasted from the http://cvs.sourceforge ...
I was just wondering, when Nice interacts heavily with Java, the resultant code contains too many curly braces which are not nice at all, as exceptions must be handled ...
Tests are performed continuously to check that the compiler is working properly. The results are http://nice.sourceforge.net/tests.html published on the website . ...
An overview of error messages that needs improvement. TOC To do Unsatisfiable constraints class A{} class B extends A{} interface I{} class X implements I{} void foo ...
Maybe a year from now, one of the most obvious special things about Nice will turn up in releases of Java and C# ( http://msdn.microsoft.com/msdnmag/issues/03/10/NET ...
It would be good to allow a different syntax for method application in some circumstances. Specifically, it would be nice if: foo { baz(); } could expand into foo ...
Requirements: A proper javac for example the Sun or IBM ones. (not the gcj-wrapper) On Debian, for example use the `java-package' package Get the Nice module from ...
Does "Modification of the type system for subclasses with a different number of type parameters than the parent." mean that I could do: class B extends A {} rather ...
The current behaviour of numeric type is to silently overflow. In some cases this is what is needed, and in some cases you don't really care, because it is guaranteed ...
I'm looking ahead to when invariants are added, and the crossover with NiceConstructors. Vague thoughts only, feel free to edit. There's an Eiffel hack to allow different ...
The compiler can be called from JVM languages (Nice, Java, ...) by using a high level API, instead of starting a different OS process (application). This is used by ...
Terms that may need explanation related to compilers and typesystems or terms that have specific meaning for Nice. General arity closure formalparameter Types monotype ...
I'm trying to summarize my ideas on constructors, based on the NiceConstructors discussions we had. I think we should separate two aspects: creation of new instances ...
Discussions of the language aspects of Nice. StandardLibrary EnumImplementation VisibilityModifiers NiceConstructors ClassInvariants PatternSyntax SingletonClasses ...
Construction is the process of creating a new object whose fields have some meaningful value. Currently, only the automaticly generated default constructor does that ...
Before a new version is released, it is sometimes available for testing purposes. List of changes You can see the latest list of changes at http://cvs.sourceforge ...
Dynamic Members This is the ability for a class to add members to their class at runtime. Actually, that's not what I'm proposing. I really want classes to be able ...
On this page the Nice Eclipse Plug-in is described TOC What is it? The "Nice Eclipse Plugin" is a plugin for the open source Eclipse IDE. It is possible to extend ...
Nice will get enums soon but first the exact implementation and syntax needs to be discussed. TOC Specification A good starting point is the java 1.5 enum proposal ...
This page is where you can propose new features for Nice. page. You can also edit a feature page to comment on the proposal. PropertySyntax InnerClasses PartialApplicationSyntax ...
It is often useful to declare that, in a subclass, a certain field of the superclass has a more precise type. To be safe, this feature only applies to final fields ...
This page shows the concept of FlagInterfaces to solve the typesafety problem of the collections api. Note: this is an immature proposal and the syntax will change ...
Note: this has been implemented, as of Nice 0.9.2 Main.DanielBonniot 18 Oct 2003 Imported email discussion: Could there be a way to also be able to separate the ...
First some simple examples how JNI in JAVA works: Test.java: class Test { static { System.loadLibrary("test"); } public static void main(String args) { System.out ...
I was wondering if it will sometime be possible to declare inner classes in Nice ? I guess this is very useful in some cases. Inner classes are currently not accepted ...
I've started a JEdit syntax mode for Nice. Installation info and downloads are available from http://www.xoltar.org/2004/aug/05/jedit-nice-mode.html on my website ...
As I am just starting to develop a new Eclipse Plugin for Nice, I wanted to share my first ideas and goals concerning features and their realization with the community ...
Here are some examples where I had to use cast or notNull in my project. BR Which problems are not solved yet in 0.8? Main.ArjanB java.util.List handles new ArrayList ...
Implementation issues of the compiler. TodoList BadErrorMessages Documentation CompilationAPI SyntanticClassHierarchy CompilerTerminology TypeErasureLimitations Building ...
When and how should the NiceCompiler(bossa. packages) be converted to Nice? It has quite a few benefits but it will require a lot of time to do so. Is the language ...
Links to proposals UPDATE: As a first step, in the current development version, classes can have initializers, like in Java. One further improvement is to allow CustomConstructors ...
It would be nice to have a tool to convert a set of Java classes to Nice syntax (e.g. stripping out the typecasts, putting argument names in all method calls, etc ...
We need a javadoc-like tool for Nice. If possible, it would be good to re-use someone else's existing backends and just write a frontend for Nice. Main.BrynKeller ...
Very Nice: mailto:subagsol@yahoo.com Chetan Mittal Nice Logo for Eclipse-Plugin: mailto:subagsol@yahoo.com Chetan Mittal Nice work! It might be good to keep some ...
This page documents the support of the type Object in Nice, which has been present since version 0.9.5 Current Situation Object is currently a sort of alien in Nice ...
This is a draft about how package repositories will be handled. Some motivation for this feature is given Doc.GetInvolved#The Community there . TOC Principle A repository ...
Parser combinators are a functional approach to parsing that uses a combination of functions to define the grammar. Some (like me) find that this leads to a pleasantly ...
Imported from SourceForge RFE #672069 Background: Many functional languages provide "curried" functions, of the form a b c, rather than the more typical (in Nice ...
We already have a comprehensive TestSuite, to check the correctness of the compiler. It would also be very useful to be able to automatically mesure performance. This ...
I was thinking if it maybe would be a good idea to let the compiler help when writing properties. What if we could let the compiler automatically create get/set functions ...
TOC Documentation for the standard library We need a documentation for the standard library. The best way would be to write a tool NiceDoc, similar to javadoc. There ...
An overview of the methods and function in stdlib for discussing semactics and as starting point of the documentation. The documentation comments are before the signatures ...
Erik Meijer and Wolfram Schulte have some interesting ideas about "stream types" which look like they could improve some of the code I've been writing. See http:/ ...
This page documents the current super mechanism in Nice. It compares it with super in Java, and calls for discussion on ehancements. First, it does not make sense ...
In an attempt to understand better the syntax part of nice, here is the class hierarchy for the bossa.syntax package. I have included questions and comments for discussion ...
A few syntax changes are being considered now. Using the same symbol for function types and anonymous functions. Making "val" an alternative for "let". "val" would ...
A TestMachine is a machine that runs the AutomatedTests for Nice. This page documents how to set up a new TestMachine. TOC Requirements To be most useful, the tests ...
This page documents the testsuite engine for Nice. It also contains a list of wanted features that are not implemented yet. TOC Nice TestSuite Documentation The Nice ...
Overview of the things left to do in random order Features VisibilityModifiers. ChangeNumberOfTypeParametersInSubclasses PropertySyntax PartialApplicationSyntax Improved ...
Current projects Doc.NiceSwing TestSuite EclipsePlugin NewEclipsePlugin Future projects (waiting for volunteers) NiceDoc NiceUnit syntax highlighting for vim update ...
An interresting idea from the Groovy language is to have an easy way to create tree like data as xml, html and user interfaces. More info: http://groovy.codehaus.org ...
This page describes the feature request to use tuple types in function declarations and anonymous functions. The following code shows the currently working way. The ...
Arjan has mentioned this a couple of times. Given that Nice is more than an OO language, do we need more than the ability to define a new object? (On a related subject ...
Generics are implemented in Nice using a technique called `type erasure' just like in Java 5. The technique and some of its repercussions are quite nicely described ...
Unknown Type I'd like to propose something that, at first glance, goes against the strongly typed phylosophy of Nice and Java. I'd like to have the ability to sometimes ...
These are aspects of the language that are not covered by the User Manual, but that should be. overview short paragraph giving the big information methods not contained ...
What should be the exact semantics of the VisibilityModifiers? Can VisibilityModifiers added to method declarations or method implementations or both? How do VisibilityModifiers ...
void return types are allowed, and void can be used in anonymous functions. However ()- still shows up in some error messages. Given the prevalence of C/Java would ...
This is the place to discuss the ongoing development of Nice. FeatureProposals CurrentDiscussions NiceCompiler ToolsAndLibraries (includes EclipsePlugin...) DevelopmentVersion ...
This is a subscription service to be automatically notified by e-mail when topics change in this Dev web. This is a convenient service, so you do not have to come ...
TWiki.Dev Web Preferences The following settings are web preferences of the TWiki.Dev web. These preferences overwrite the site-level preferences in TWIKIWEB . WIKIPREFSTOPIC ...
TWiki's Dev web SCRIPTURL /view SCRIPTSUFFIX /Dev The Dev web of TWiki. TWiki is a Web-Based Collaboration Platform for the Corporate World. INCLUDE{" TWIKIWEB .WebRssBase ...
Statistics for Dev Web Month: Topic views: Topic saves: File uploads: Most popular topic views: Top contributors for topic save and uploads: Aug 2005 1736 0 0 116 ...
Generalities A test is a procedure that checks some functionality of the Nice compiler. Format Each test consists of an executable file. It must be placed in the ...
Number of topics: 70
See also the faster WebTopicList
This is a subscription service to be automatically notified by e-mail when topics change in this Dev web. This is a convenient service, so you do not have to come back and check all the time if something has changed. To subscribe, please add a bullet with your WikiName in alphabetical order to this list:
Format: <space><space><space>, followed by: * Main.yourWikiName (if you want that the e-mail address in your home page is used) * Main.yourWikiName - yourEmailAddress (if you want to specify a different e-mail address) * Main.anyTWikiGroup (if you want to notify all members of a particular TWikiGroup)
Related topics:TWikiUsers, TWikiRegistration
The following settings are web preferences of the TWiki.Dev web. These preferences overwrite the site-level preferences in TWikiPreferences, and can be overwritten by user preferences (your personal topic, i.e. TWikiGuest in the TWiki.Main web)
Preferences:
If yes, set SITEMAPLIST to on, do not set NOSEARCHALL, and add the "what" and "use to..." description for the site map. Make sure to list only links that include the name of the web, e.g. Dev.Topic links.
Set SITEMAPLIST = on
Set SITEMAPWHAT = Topics on Nice development
Set SITEMAPUSETO = ...collaborate on Nice development
Exclude web from a web="all" search: (Set to on for hidden webs)
Set NOSEARCHALL =
Default template for new topics and form(s) for this web:
WebTopicEditTemplate?: Default template for new topics in this web. (Site-level is used if topic does not exist)
Users or groups who are not / are allowed to view / change / rename topics in the Dev web: (See TWikiAccessControl)
Set DENYWEBVIEW =
Set ALLOWWEBVIEW =
Set DENYWEBCHANGE =
Set ALLOWWEBCHANGE =
Set DENYWEBRENAME =
Set ALLOWWEBRENAME =
Users or groups allowed to change or rename this WebPreferences topic: (I.e. TWikiAdminGroup)
Set ALLOWTOPICCHANGE =
Set ALLOWTOPICRENAME =
Web preferences that are not allowed to be overridden by user preferences:
Set FINALPREFERENCES = WEBTOPICLIST, DENYWEBVIEW, ALLOWWEBVIEW, DENYWEBCHANGE, ALLOWWEBCHANGE, DENYWEBRENAME, ALLOWWEBRENAME
Notes:
A preference is defined as: 6 spaces * Set NAME = value Example:
Set WEBBGCOLOR = #FFFFC0
Preferences are used as TWikiVariables by enclosing the name in percent signs. Example:
When you write variable %WEBBGCOLOR% , it gets expanded to #D0D0D0 .
The sequential order of the preference settings is significant. Define preferences that use other preferences first, i.e. set WEBCOPYRIGHT before WIKIWEBMASTER since %WEBCOPYRIGHT% uses the %WIKIWEBMASTER% variable.
You can introduce new preferences variables and use them in your topics and templates. There is no need to change the TWiki engine (Perl scripts).
TWiki's Dev web
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev
The Dev web of TWiki. TWiki is a Web-Based Collaboration Platform for the Corporate World.en-usCopyright 2009, Peter Thoeny and contributing authors.Peter Thoeny [Peter@Thoeny.com]Peter Thoeny [Peter@Thoeny.com]TWikiTWiki.DevTWiki.Dev
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev
http://nice.sourceforge.net/twiki/pub/TWiki/TWikiLogos/twikilogo88x31.gifWebStatistics
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/WebStatistics
Statistics for Dev Web Month: Topic views: Topic saves: File uploads: Most popular topic views: Top contributors for topic save and uploads: Aug 2005 1736 0 0 116 ...2005-08-14T01:47:00Zguest1.487updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/WebStatisticshttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/WebStatisticsUserManualOmissions
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/UserManualOmissions
These are aspects of the language that are not covered by the User Manual, but that should be. overview short paragraph giving the big information methods not contained ...2005-07-07T10:05:00ZDanielBonniot1.43updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/UserManualOmissionshttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/UserManualOmissionsStandardLibrary
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/StandardLibrary
TOC Documentation for the standard library We need a documentation for the standard library. The best way would be to write a tool NiceDoc, similar to javadoc. There ...2005-07-02T10:04:00Zguest1.17updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/StandardLibraryhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/StandardLibraryNiceConverter
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/NiceConverter
It would be nice to have a tool to convert a set of Java classes to Nice syntax (e.g. stripping out the typecasts, putting argument names in all method calls, etc ...2005-06-23T00:11:00Zguest1.1updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/NiceConverterhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/NiceConverterFeatureProposals
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/FeatureProposals
This page is where you can propose new features for Nice. page. You can also edit a feature page to comment on the proposal. PropertySyntax InnerClasses PartialApplicationSyntax ...2005-06-23T00:08:00Zguest1.19updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/FeatureProposalshttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/FeatureProposalsTypeErasureLimitations
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/TypeErasureLimitations
Generics are implemented in Nice using a technique called `type erasure' just like in Java 5. The technique and some of its repercussions are quite nicely described ...2005-04-30T12:14:00Zguest1.1updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/TypeErasureLimitationshttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/TypeErasureLimitationsNiceCompiler
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/NiceCompiler
Implementation issues of the compiler. TodoList BadErrorMessages Documentation CompilationAPI SyntanticClassHierarchy CompilerTerminology TypeErasureLimitations Building ...2005-04-30T12:04:00Zguest1.6updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/NiceCompilerhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/NiceCompilerEnhancedNullTesting
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/EnhancedNullTesting
http://sourceforge.net/tracker/index.php?func detail aid 671444 group id 12788 atid 362788 Section "Non-local variables" in http://nice.sourceforge.net/cgi-bin/twiki ...2005-04-11T14:50:00Zguest1.1updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/EnhancedNullTestinghttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/EnhancedNullTestingBootstrappingFromCVS
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/BootstrappingFromCVS
Requirements: A proper javac for example the Sun or IBM ones. (not the gcj-wrapper) On Debian, for example use the `java-package' package Get the Nice module from ...2005-04-11T12:48:00Zguest1.3updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/BootstrappingFromCVShttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/BootstrappingFromCVSDevelopmentVersion
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/DevelopmentVersion
Before a new version is released, it is sometimes available for testing purposes. List of changes You can see the latest list of changes at http://cvs.sourceforge ...2005-04-06T13:36:00Zguest1.3updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/DevelopmentVersionhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/DevelopmentVersionCheckedIntegerArithmetic
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/CheckedIntegerArithmetic
The current behaviour of numeric type is to silently overflow. In some cases this is what is needed, and in some cases you don't really care, because it is guaranteed ...2005-01-17T20:08:00ZMotiN1.12updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/CheckedIntegerArithmetichttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/CheckedIntegerArithmeticEnumImplementation
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/EnumImplementation
Nice will get enums soon but first the exact implementation and syntax needs to be discussed. TOC Specification A good starting point is the java 1.5 enum proposal ...2005-01-16T15:22:00ZMotiN1.8updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/EnumImplementationhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/EnumImplementationVisibilityModifiers
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/VisibilityModifiers
What should be the exact semantics of the VisibilityModifiers? Can VisibilityModifiers added to method declarations or method implementations or both? How do VisibilityModifiers ...2005-01-12T03:16:00ZRohanHart1.15updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/VisibilityModifiershttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/VisibilityModifiersEclipsePlugin
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/EclipsePlugin
On this page the Nice Eclipse Plug-in is described TOC What is it? The "Nice Eclipse Plugin" is a plugin for the open source Eclipse IDE. It is possible to extend ...2005-01-10T06:47:00Zguest1.35updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/EclipsePluginhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/EclipsePluginTreeDataConstruction
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/TreeDataConstruction
An interresting idea from the Groovy language is to have an easy way to create tree like data as xml, html and user interfaces. More info: http://groovy.codehaus.org ...2005-01-03T00:09:00ZMarkCC1.6updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/TreeDataConstructionhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/TreeDataConstructionBigPictureWhatIsItGoodFor
http://nice.sourceforge.net/cgi-bin/twiki/view/Dev/BigPictureWhatIsItGoodFor
Maybe a year from now, one of the most obvious special things about Nice will turn up in releases of Java and C# ( http://msdn.microsoft.com/msdnmag/issues/03/10/NET ...2004-12-22T16:33:00ZMikeBeckerle1.11updatedmajorhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/BigPictureWhatIsItGoodForhttp://nice.sourceforge.net/cgi-bin/twiki/rdiff/Dev/BigPictureWhatIsItGoodFor
Each test consists of an executable file. It must be placed in the toplevel
directory of the framework. The file name must be of the form:
.test
Most likely, it will consist of a shell script, that drives the
compilation of some code, and returns an exit code to report how it
went.
The script can use the following environment variables:
NICE_JAR:
The location of the 'nice.jar' files, which contains the fresh compiler
to be tested.
CVS_SOURCEFORGE:
The sourceforge CVS host to use.
JDK_VERSION:
Version of the JDK that is going to be used, e.g. "1.4.1".
This variable might not be set, in which case a recent version
of the JDK can be expected.
This is useful to skip some a test who require a certain version of the
JDK.
1: FAIL
There was a problem. It might be a bug in the compiler, or in the test.
2: SKIP
This test is not applicable to the current configuration.
For instance, you can use this not to run a test that requires
a certain version of the JDK, which is not present in the current
configuration.
3: IMPOSSIBLE
The test could not be executed for some reason. Ignore it.
For instance, this should be used if the test needs to download
some data or source code, and could not do it because the network
is not working.
The test script should output on standard out detailed information
about the execution of the test. This output will be stored, and can
be used to diagnose why a test failed.
-- DanielBonniot - 22 Jun 2003
Number of topics: 70